Multithreading in winforms application

I am writing winnings that use the report viewer to create multiple PDFs. These PDF files are divided into 4 main parts, each part is responsible for creating a specific report. These processes create a minimum of 1 file up to the number of users (currently 50).

The program already exists using 4 methods in sequence. For added performance, when the number of users grows, I want to separate these methods from the mail process in 4 separate threads.

While I'm new to multithreading using C #, I read a number of articles on how to do this. The only thing I'm not sure about is how I should start. When I read a few blog posts, I'm not sure if using 4 separate threads, a thread pool, or multiple background workers. (or should there be optimal concurrent programming?). Blog posts tell me if more than 3 threads use a thread pool, but then tell me using winforms, use backgroundworker. Which option is better (and why)?

In the end, my main thread should wait for all processes to complete before continuing.

Can someone tell me what is the best solution for my problem.

* Additional information after editing *

What I forgot to say (after I read your comments and possible solutions). Methods share one read-only IEnumerable. After running methods (which should not be run sequentially), the methods fire events to send status updates to the user interface. I think that triggering events is difficult, if not impossible, using separate threads, so there needs to be some kind of callback function to report status updates during operation.

some example in psuedo code.

main() { private List<customclass> lcc = importCustomClass() export.CreatePDFKind1.create(lcc.First(), exportfolderpath, arg1) export.CreatePDFKind2.create(lcc, exportfolderpath) export.CreatePDFKind3.create(lcc.First(), exportfolderpath) export.CreatePDFKind4.create(customclass2, exportfolderpath) } namespace export { class CreatePDFKind1 { create(customclass cc, string folderpath) { do something; reportstatus(listviewItem, status, message) } } class CreatePDFKind2 { create(IEnumerable<customclass> lcc, string folderpath) { foreach (var x in lcc) { do something; reportstatus(listviewItem, status, message) } } } etc....... } 
+4
source share
3 answers

From the most basic picture that you described, I would use the Task Paralell Library (TPL) . Comes with .NET Framework 4.0+.

You are talking about the β€œbest” way to use thread pools when spawning a large number of threads. Although this is correct [the most efficient way of manging resources], TPL does all this for you - without having to worry about anything. TPL also uses several threads and waits for their completion to be also a boat ...

To do what you need, I will use TPL and Continuations . Continuing not only allows you to create a task flow, but also handle your exceptions. This is a great introduction to TPL. But to give you some idea ...

You can run the TPL task using

 Task task = Task.Factory.StartNew(() => { // Do some work here... }); 

Now, to start the second task when the previous task is completed (by mistake or successfully), you can use the ContinueWith method

 Task task1 = Task.Factory.StartNew(() => Console.WriteLine("Antecedant Task")); Task task2 = task1.ContinueWith(antTask => Console.WriteLine("Continuation...")); 

So, as soon as task1 completes, task2 'fires-up' fails or is canceled and starts. Note that if task1 completed before the second line of task2 code was scheduled for immediate execution. The antTask argument passed to the second lambda is a reference to the previous task. See this link for more detailed examples ...

You can also pass on the results of continuing the antecedent task

 Task.Factory.StartNew<int>(() => 1) .ContinueWith(antTask => antTask.Result * 4) .ContinueWith(antTask => antTask.Result * 4) .ContinueWith(antTask =>Console.WriteLine(antTask.Result * 4)); // Prints 64. 

Note. Be sure to check out the exception handling in the first link, as this may cause a newcomer to TPL to go down.

The last thing you need to look, in particular, at what you want is children's tasks. Children are those created as AttachedToParent . In this case, the continuation will not be executed until all child tasks are completed.

 TaskCreationOptions atp = TaskCreationOptions.AttachedToParent; Task.Factory.StartNew(() => { Task.Factory.StartNew(() => { SomeMethod() }, atp); Task.Factory.StartNew(() => { SomeOtherMethod() }, atp); }).ContinueWith( cont => { Console.WriteLine("Finished!") }); 

So, in your case, you will start your four tasks, and then wait for them to complete in the main thread.

Hope this helps.

+5
source

Using BackgroundWorker is useful if you need to interact with the user interface regarding a background process. If you do not, I would not bother with this. You can immediately start 4 Task objects:

 tasks.Add(Task.Factory.StartNew(()=>DoStuff())); tasks.Add(Task.Factory.StartNew(()=>DoStuff2())); tasks.Add(Task.Factory.StartNew(()=>DoStuff3())); 

If you need to interact with the user interface; perhaps updating it to reflect when tasks are completed, I would suggest looking at one BackgroundWorker and then using the tasks again to process each individual unit of work. Since there is some additional overhead when using BackgroundWorker , I would not run many of them if you can avoid this.

 BackgroundWorker bgw = new BackgroundWorker(); bgw.DoWork += (_, args) => { List<Task> tasks = new List<Task>(); tasks.Add(Task.Factory.StartNew(() => DoStuff())); tasks.Add(Task.Factory.StartNew(() => DoStuff2())); tasks.Add(Task.Factory.StartNew(() => DoStuff3())); Task.WaitAll(tasks.ToArray()); }; bgw.RunWorkerCompleted += (_, args) => updateUI(); bgw.RunWorkerAsync(); 

Of course, you could only use Task methods to do all this, but I still find BackgroundWorkers easier to use for simpler cases. Using .NEt 4.5, you can use Task.WhenAll to start a continuation in the user interface thread when all 4 tasks are completed, but doing this in 4.0 will not be so simple.

+1
source

It is impossible to say without additional information. The fact that they are in four separate methods does not really matter if they access the same resources. For example, a PDF file. If you are having trouble understanding what I mean, you should post a piece of code for each method, and I'll tell you a little more.

Since the number of "parts" that you have is fixed, it will not make much difference whether you use separate threads, background workers or use a thread pool. I'm not sure why people recommend background workers. Most likely because this is a simpler approach to multithreading and more difficult to use.

0
source

Source: https://habr.com/ru/post/1445801/


All Articles