patterncsharpMinor
Creating multiple threads to process items in a listview
Viewed 0 times
processcreatingthreadsitemsmultiplelistview
Problem
The goal is to pick items from a listview, do some web related task with the information and then update the UI with the returned information from the web request.
The problem is with having multiple list view items processed at once, me being a newbie to C#, i came up with this code, I'm pretty sure this is not good, i just came up with it, is there a better way to get this done ?
```
Dictionary worker_list = new Dictionary();
int completed_count = 0;
int total_work;
public Form1()
{
InitializeComponent();
}
public void worker(String woker_id)
{
WebClient client = new WebClient();
byte[] response =
client.UploadValues("http://google.com", new NameValueCollection() { });
worker_list[woker_id].ReportProgress(100);
if(completed_count != total_work){
worker(woker_id);
}
}
// Adding the work to the que since a thread can't access background Ui thread
private void button2_Click(object sender, EventArgs e)
{
// preparing task list
txtOutput.Text += "Total stuff to run" + listView1.Items.Count + Environment.NewLine + Environment.NewLine;
// check for valid input
int value;
if (!int.TryParse(textBox1.Text, out value) || textBox1.Text == "0"){
MessageBox.Show("invalid number of threads");
return;
}
// create threads
for (int i = 0; i < value; i++)
{
String woker_id = "bg_" + i.ToString();
worker_list[woker_id] = new BackgroundWorker();
worker_list[woker_id].WorkerSupportsCancellation = true;
worker_list[woker_id].WorkerReportsProgress = true;
// Intiial message
txtOutput.Text += "Worker " + woker_id + " Started" + Environment.NewLine;
worker_list[woker_id].DoWork += delegate {
worker(woker_id);
};
worker_list[woker_id].ProgressChanged += delegate {
if(completed_count == total_work) {
return;
}else
{
txtOutput.Text += "Worker "
The problem is with having multiple list view items processed at once, me being a newbie to C#, i came up with this code, I'm pretty sure this is not good, i just came up with it, is there a better way to get this done ?
```
Dictionary worker_list = new Dictionary();
int completed_count = 0;
int total_work;
public Form1()
{
InitializeComponent();
}
public void worker(String woker_id)
{
WebClient client = new WebClient();
byte[] response =
client.UploadValues("http://google.com", new NameValueCollection() { });
worker_list[woker_id].ReportProgress(100);
if(completed_count != total_work){
worker(woker_id);
}
}
// Adding the work to the que since a thread can't access background Ui thread
private void button2_Click(object sender, EventArgs e)
{
// preparing task list
txtOutput.Text += "Total stuff to run" + listView1.Items.Count + Environment.NewLine + Environment.NewLine;
// check for valid input
int value;
if (!int.TryParse(textBox1.Text, out value) || textBox1.Text == "0"){
MessageBox.Show("invalid number of threads");
return;
}
// create threads
for (int i = 0; i < value; i++)
{
String woker_id = "bg_" + i.ToString();
worker_list[woker_id] = new BackgroundWorker();
worker_list[woker_id].WorkerSupportsCancellation = true;
worker_list[woker_id].WorkerReportsProgress = true;
// Intiial message
txtOutput.Text += "Worker " + woker_id + " Started" + Environment.NewLine;
worker_list[woker_id].DoWork += delegate {
worker(woker_id);
};
worker_list[woker_id].ProgressChanged += delegate {
if(completed_count == total_work) {
return;
}else
{
txtOutput.Text += "Worker "
Solution
We actually don't use the
Briefly you need to (ok, you should) put the download logic into a new class that could have a
If you want to be able to report progress or just be notified when a task is finished you can use the
Your form code could then be reduced to a simple event handler that is also marked with
When a task is finished, the form receives a progress report (that can be seen here as a notification) via the
You may also use a different pattern if you want to process each task result right away by doing it as soon as any task is finished with
BackgroundWorker anymore. The same thing can be done much easier with the newer Task Parallel Library (TPL).Briefly you need to (ok, you should) put the download logic into a new class that could have a
Download method. It needs to be marked with async and return a Task to be awaitable. But you need a result so you can use it with a generic parameter Task. (Sidenote: remember to dispose the WebClient). For the new download to work you need to use the awaitable upload API. If you want to be able to report progress or just be notified when a task is finished you can use the
IProgress interface as one of the parameters. You call it inside the method to report its progress. Here just an arbitrary task-id. class Downloader
{
public async Task Download(int taskId, NameValueCollection data, IProgress progress)
{
using (var client = new WebClient())
{
var response = await client.UploadValuesTaskAsync("http://google.com", data);
progress.Report(taskId);
return response;
}
}
}Your form code could then be reduced to a simple event handler that is also marked with
async. It prepares a collection of download-tasks and then awaits them all or processes each one when it's finished (your choice).When a task is finished, the form receives a progress report (that can be seen here as a notification) via the
ProgressChanged event of an instance of the Progress class.class MyForm : Form
{
private readonly Downloader _downloader = new Downloader();
private async void button2_Click(object sender, EventArgs e)
{
// Initialize progress handling.
var progress = new Progress();
progress.ProgressChanged += (downloader, taskId) =>
{
// update label...
};
var downloadCount = 2; //... get the count
var downloadTasks =
Enumerable
.Range(0, downloadCount)
// Create task specific arguments.
.Select((x, i) => _downloader.Download(i, new NameValueCollection(), progress));
var results = await Task.WhenAll(downloadTasks);
}
}You may also use a different pattern if you want to process each task result right away by doing it as soon as any task is finished with
WhenAny and a loop:for (int i = 0; i < downloadCount; i++)
{
var firstTask = await Task.WhenAny(downloadTasks);
var result = await firstTask;
// process task result...
}Code Snippets
class Downloader
{
public async Task<byte[]> Download(int taskId, NameValueCollection data, IProgress<int> progress)
{
using (var client = new WebClient())
{
var response = await client.UploadValuesTaskAsync("http://google.com", data);
progress.Report(taskId);
return response;
}
}
}class MyForm : Form
{
private readonly Downloader _downloader = new Downloader();
private async void button2_Click(object sender, EventArgs e)
{
// Initialize progress handling.
var progress = new Progress<int>();
progress.ProgressChanged += (downloader, taskId) =>
{
// update label...
};
var downloadCount = 2; //... get the count
var downloadTasks =
Enumerable
.Range(0, downloadCount)
// Create task specific arguments.
.Select((x, i) => _downloader.Download(i, new NameValueCollection(), progress));
var results = await Task.WhenAll(downloadTasks);
}
}for (int i = 0; i < downloadCount; i++)
{
var firstTask = await Task.WhenAny(downloadTasks);
var result = await firstTask;
// process task result...
}Context
StackExchange Code Review Q#151140, answer score: 8
Revisions (0)
No revisions yet.