Multithreading programming learning notes-using concurrent sets (2), multithreading programming learning notes
Multi-thread programming learning notes-using concurrent sets (1)
Ii. Use ConcurrentQueue for asynchronous Processing
This example shows how to create a group of tasks that can be asynchronously processed by multiple threads.
I. Sample Code of the program:
Using System; using System. collections. generic; using System. linq; using System. text; using System. threading. tasks; using System. collections. concurrent; using System. diagnostics; using System. threading; namespace ThreadCollectionDemo {class Program {const string item = "Dict Name"; public static string CurrentItem; static double time1; static void Main (string [] args) {Console. writeLine (string. format ("-- --- ConcurrentQueue operation ---- "); Task task = TaskRun1 (); task. wait (); Console. read ();} private static async Task TaskRun1 () {var queue = new ConcurrentQueue <CustomTask> (); var cts = new CancellationTokenSource (); var taskSrc = Task. run () => TaskProduct (queue); Task [] process = new Task [4]; for (int I = 1; I <= 4; I ++) {string processId = I. toString (); process [I-1] = Task. run () => TaskProcess (Queue, "Processer" + processId, cts. token);} await taskSrc; cts. cancelAfter (TimeSpan. fromSeconds (2); await Task. whenAll (process);} static async Task TaskProduct (ConcurrentQueue <CustomTask> queue) {for (int I = 0; I <20; I ++) {await Task. delay (50); var workitem = new CustomTask {Id = I}; queue. enqueue (workitem); Console. writeLine (string. format ("add the {0} element to ConcurrentQueue", workitem. id ));}} Static async Task TaskProcess (ConcurrentQueue <CustomTask> queue, string name, CancellationToken token) {CustomTask workitem; bool token = false; await GetRandomDely (); do {dequeueSuccesfl = queue. tryDequeue (out workitem); if (dequeueSuccesfl) {Console. writeLine (string. format ("element {0} retrieved from ConcurrentQueue, name: {1}", workitem. id, name);} await GetRandomDely ();} while (! Token. isCancellationRequested);} static Task GetRandomDely () {int dely = new Random (DateTime. now. millisecond ). next (1, 1000); return Task. delay (dely) ;}} public class CustomTask {public int Id {get; set ;}}}
2. The program running result is shown in.
When the program runs, we use the ConcurrentQueue set to create a task queue. Then a cancel sign is created to stop the task after we put the task into the queue. Next, a separate working thread is started to put the task into the task queue. This part is divided into asynchronous processing to produce the amount of work.
Now define the part of the consumption task in this program. We have created four working threads, which will wait for a period of time at random, then obtain a task from the task queue, process the task, and repeat the whole process until we send a sign of cancellation. Finally, we start the thread that generates the task and wait for the thread to complete. Then we used the cancel sign to send a signal to the consumer. The last step is to wait for all consumption to complete.
We can see that the tasks in the queue are processed in the order from front to back, but a subsequent task may be processed first than the previous task, because we have four working threads running independently, and the task processing time is not constant. We can see that accessing this queue is thread-safe, and no element is extracted twice.
Ii. Changing the asynchronous processing sequence of ConcurrentStack
This example is a modified version of the previous example. We have created a group of tasks that are asynchronously processed by multiple worker threads, but this time we use ConcurrentStack to implement it. Let's take a look at the differences between the two examples.
1. The program code is as follows.
Using System; using System. collections. generic; using System. linq; using System. text; using System. threading. tasks; using System. collections. concurrent; using System. diagnostics; using System. threading; namespace ThreadCollectionDemo {class Program {static void Main (string [] args) {Console. writeLine (string. format ("----- ConcurrentStack operation ----"); Task task = TaskStack (); task. wait (); Console. read ();} p Rivate static async Task TaskStack () {var stack = new ConcurrentStack <CustomTask> (); var cts = new CancellationTokenSource (); var taskSrc = Task. run () => TaskProduct (stack); Task [] process = new Task [4]; for (int I = 1; I <= 4; I ++) {string processId = I. toString (); process [I-1] = Task. run () => TaskProcess (stack, "Processer" + processId, cts. token);} await taskSrc; cts. cancelAfter (TimeSp An. fromSeconds (2); await Task. whenAll (process);} static async Task TaskProduct (ConcurrentStack <CustomTask> stack) {for (int I = 0; I <20; I ++) {await Task. delay (50); var workitem = new CustomTask {Id = I}; stack. push (workitem); Console. writeLine (string. format ("add the {0} element to ConcurrentStack", workitem. id) ;}} static async Task TaskProcess (ConcurrentStack <CustomTask> stack, string name, Cancellati OnToken token) {CustomTask workitem; bool popSuccesful = false; await GetRandomDely (); do {popSuccesful = stack. tryPop (out workitem); if (popSuccesful) {Console. writeLine (string. format ("element {0} ConcurrentStack retrieved, name: {1}", workitem. id, name);} await GetRandomDely ();} while (! Token. isCancellationRequested);} static Task GetRandomDely () {int dely = new Random (DateTime. now. millisecond ). next (1, 1000); return Task. delay (dely );}}}
2. The running result of the program is as follows.
When the program runs, we create an instance of the ConcurrentStack set. The rest of the code is almost the same as the previous example. The only difference is that we use the Push and TryPop methods for the concurrent stack, while the Enqueue and TryDequeue methods for the concurrent queue.
The order of task processing that can be pulled from the result is changed. A stack is a LIFO set. A worker thread first processes the most recent task. In a concurrent queue, tasks are processed in almost the same order as those added. This indicates that, based on the number of worker threads, We will process the first created task within a certain period of time. In the stack, the previously created task has a lower priority and is not processed until the producer stops putting more tasks into the stack. This behavior is definite, and it is best to use the queue in this scenario.