This article summarizes and debugs some of the code examples in the article for a personal study of the Advanced tutorial on C # parallel programming. Can be used later in the development process.
For parallel tasks, it is closely related to parallel access to some shared resources, data structures. The most common thing to do is to lock up some queues-unlock them, and then perform mutex operations like insertions, deletions, and so on. NETFramework 4.0 provides a number of packaged support parallel operation data containers that can reduce the complexity of parallel programming.
Basic information
. namespaces for parallel collections in NETFramework: System.Collections.Concurrent
Parallel containers:
- Concurrentqueue
- Concurrentstack
- Concurrentbag: An unordered set of data structures that is useful when you don't need to consider order.
- BlockingCollection: Similar to the classic blocking queue data structure
- Concurrentdictionary
These collections use a lock-free technique (CAS compare-and-swap and memory barrier Memories Barrier) to some extent, and gain performance gains over mutex locks. In a serial program, however, it is best not to use these collections, which inevitably affect performance.
About CAS:
- http://www.tuicool.com/articles/zuui6z
- Http://www.360doc.com/content/11/0914/16/7656248_148221200.shtml
About Memory barriers
- Http://en.wikipedia.org/wiki/Memory_barrier
Usage and example Concurrentqueue are completely unlocked, but may get stuck in spin and retry operations when CAS face resource contention failures.
- Enqueue: Inserting elements at the end of a team
- Trydequeue: Attempt to delete the team header element and return through the out parameter
- Trypeek: Attempts to return the head element through the out parameter, but does not delete the element.
Examples of programs:
Using system;using system.text;using system.threading.tasks;using system.collections.concurrent;namespace Sample4_1 _concurrent_queue{class Program {internal static concurrentqueue<int> _testqueue; Class ThreadWork1//producer {public ThreadWork1 () {} public void run () {System.Console.WriteLine ("ThreadWork1 run {"); for (int i = 0; i < i++) {System.Console.WriteLine ("ThreadWork1 producer:" + I ); _testqueue.enqueue (i); } System.Console.WriteLine ("ThreadWork1 Run}"); }} class ThreadWork2//consumer {public ThreadWork2 () {} public void run () {int i = 0; BOOL Isdequeuue = false; System.Console.WriteLine ("ThreadWork2 run {"); for (;;) {Isdequeuue = _testqueue.trydequeue (out i); if (Isdequeuue) System.Console.WriteLine ("ThreadWork2 Consumer:" + i * i + "====="); if (i = =) break; } System.Console.WriteLine ("ThreadWork2 Run}"); }} static void StartT1 () {ThreadWork1 work1 = new ThreadWork1 (); Work1.run (); } static void StartT2 () {THREADWORK2 work2 = new ThreadWork2 (); Work2.run (); } static void Main (string[] args) {Task T1 = new Task (() = StartT1 ()); Task t2 = new Task (() = StartT2 ()); _testqueue = new concurrentqueue<int> (); Console.WriteLine ("Sample 3-1 Main {"); Console.WriteLine ("Main T1 T2 started {"); T1. Start (); T2. Start (); Console.WriteLine ("Main T1 T2 started} "); Console.WriteLine ("Main wait T1 T2 end {"); Task.waitall (t1, T2); Console.WriteLine ("Main wait T1 T2 End}"); Console.WriteLine ("Sample 3-1 Main}"); Console.readkey (); } }}
Concurrentstack is completely unlocked, but it may get stuck in a spin and retry operation when the CAs face resource contention failure.
- Push: Inserts an element into the top of the stack
- Trypop: Eject element from top of stack and return by out parameter
- Trypeek: Returns the top element of the stack, but does not eject.
Examples of programs:
Using system;using system.text;using system.threading.tasks;using system.collections.concurrent;namespace Sample4_2 _concurrent_stack{class Program {internal static concurrentstack<int> _teststack; Class ThreadWork1//producer {public ThreadWork1 () {} public void run () {System.Console.WriteLine ("ThreadWork1 run {"); for (int i = 0; i < i++) {System.Console.WriteLine ("ThreadWork1 producer:" + I ); _teststack.push (i); } System.Console.WriteLine ("ThreadWork1 Run}"); }} class ThreadWork2//consumer {public ThreadWork2 () {} public void run () {int i = 0; BOOL Isdequeuue = false; System.Console.WriteLine ("ThreadWork2 run {"); for (;;) {Isdequeuue = _teststack.trypop (out i); if (Isdequeuue) System.Console.WriteLine ("ThreadWork2 Consumer:" + i * i + "=====" + i); if (i = =) break; } System.Console.WriteLine ("ThreadWork2 Run}"); }} static void StartT1 () {ThreadWork1 work1 = new ThreadWork1 (); Work1.run (); } static void StartT2 () {THREADWORK2 work2 = new ThreadWork2 (); Work2.run (); } static void Main (string[] args) {Task T1 = new Task (() = StartT1 ()); Task t2 = new Task (() = StartT2 ()); _teststack = new concurrentstack<int> (); Console.WriteLine ("Sample 4-1 Main {"); Console.WriteLine ("Main T1 T2 started {"); T1. Start (); T2. Start (); Console.WriteLine ("Main t1 T2 started} "); Console.WriteLine ("Main wait T1 T2 end {"); Task.waitall (t1, T2); Console.WriteLine ("Main wait T1 T2 End}"); Console.WriteLine ("Sample 4-1 Main}"); Console.readkey (); } }}
An interesting phenomenon in the test:
Although the producer has already inserted the value in the stack to 25, the consumer first out of the stack is actually 4, not 25. It's like a mistake. But think about the stack, the stack and print statements are two parts, and not atomic operation, it should be normal to appear in this phenomenon.
Sample 3-1 Main {
Main T1 T2 started {
Main T1 T2 started}
Main wait T1 T2 End {
ThreadWork1 Run {
ThreadWork1 producer:0
ThreadWork2 Run {
ThreadWork1 producer:1
ThreadWork1 Producer:2
ThreadWork1 Producer:3
ThreadWork1 Producer:4
ThreadWork1 Producer:5
ThreadWork1 Producer:6
ThreadWork1 Producer:7
ThreadWork1 Producer:8
ThreadWork1 Producer:9
ThreadWork1 producer:10
ThreadWork1 producer:11
ThreadWork1 Producer:12
ThreadWork1 producer:13
ThreadWork1 producer:14
ThreadWork1 producer:15
ThreadWork1 producer:16
ThreadWork1 producer:17
ThreadWork1 producer:18
ThreadWork1 producer:19
ThreadWork1 producer:20
ThreadWork1 producer:21
ThreadWork1 producer:22
ThreadWork1 producer:23
ThreadWork1 producer:24
ThreadWork1 producer:25
ThreadWork2 consumer:16 =====4
THREADWORK2 consumer:625 =====25
THREADWORK2 consumer:576 =====24
THREADWORK2 consumer:529 =====23
ThreadWork1 producer:26
ThreadWork1 producer:27
ThreadWork1 producer:28
Concurrentbag an unordered collection in which the program can insert an element, or delete an element. When inserting into a collection in the same thread, it is highly efficient to delete elements.
ADD: inserting elements into the collection
TryTake: Remove the element from the collection and delete
Trypeek: Removes the element from the collection, but does not delete the element.
Examples of programs:
Using system;using system.text;using system.threading.tasks;using system.collections.concurrent;namespace Sample4_3 _concurrent_bag{class Program {internal static concurrentbag<int> _testbag; Class ThreadWork1//producer {public ThreadWork1 () {} public void run () {System.Console.WriteLine ("ThreadWork1 run {"); for (int i = 0; i < i++) {System.Console.WriteLine ("ThreadWork1 producer:" + I ); _testbag.add (i); } System.Console.WriteLine ("ThreadWork1 Run}"); }} class ThreadWork2//consumer {public ThreadWork2 () {} public void run () {int i = 0; int ncnt = 0; BOOL Isdequeuue = false; System.Console.WriteLine ("ThreadWork2 run {"); FoR (;;) {Isdequeuue = _testbag.trytake (out i); if (Isdequeuue) {System.Console.WriteLine ("THREADWORK2 Consumer:" + i * i + " ===== "+ i); ncnt++; } if (ncnt = =) break; } System.Console.WriteLine ("ThreadWork2 Run}"); }} static void StartT1 () {ThreadWork1 work1 = new ThreadWork1 (); Work1.run (); } static void StartT2 () {THREADWORK2 work2 = new ThreadWork2 (); Work2.run (); } static void Main (string[] args) {Task T1 = new Task (() = StartT1 ()); Task t2 = new Task (() = StartT2 ()); _testbag = new concurrentbag<int> (); Console.WriteLine ("Sample 4-3 Main {"); Console.WriteLine ("Main T1 T2 Started {"); T1. Start (); T2. Start (); Console.WriteLine ("Main T1 T2 started}"); Console.WriteLine ("Main wait T1 T2 end {"); Task.waitall (t1, T2); Console.WriteLine ("Main wait T1 T2 End}"); Console.WriteLine ("Sample 4-3 Main}"); Console.readkey (); } }}
BlockingCollection a container that supports boundaries and blocking
- ADD: Inserting elements into the container
- TryTake: Remove the element from the container and delete
- Trypeek: Removes the element from the container without deleting it.
- CompleteAdding: Tells the container to add elements to complete. An exception occurs at this point if you still want to continue adding.
- IsCompleted: tells the consumer thread that the producer thread is still running and the task is not completed.
Sample program:
In the program, the consumer thread fully uses while (!_testbcollection.iscompleted) as the judging condition for exiting the run. In Worker1, two statements are commented out, and when I is 50 the completeadding is set, but when you continue to insert the element, the system throws an exception, prompting that the insertion cannot continue.
Using system;using system.text;using system.threading.tasks;using system.collections.concurrent;namespace Sample4_4 _concurrent_bag{class Program {internal static blockingcollection<int> _testbcollection; Class ThreadWork1//producer {public ThreadWork1 () {} public void run () {System.Console.WriteLine ("ThreadWork1 run {"); for (int i = 0; i < i++) {System.Console.WriteLine ("ThreadWork1 producer:" + I ); _testbcollection.add (i); if (i = =)//_testbcollection.completeadding (); } _testbcollection.completeadding (); System.Console.WriteLine ("ThreadWork1 Run}"); }} class ThreadWork2//consumer {public ThreadWork2 () {} public void Run () { int i = 0; int ncnt = 0; BOOL Isdequeuue = false; System.Console.WriteLine ("ThreadWork2 run {"); while (!_testbcollection.iscompleted) {Isdequeuue = _testbcollection.trytake (out i); if (Isdequeuue) {System.Console.WriteLine ("ThreadWork2 consum ER: "+ i * i +" ===== "+ i); ncnt++; }} System.Console.WriteLine ("ThreadWork2 Run}"); }} static void StartT1 () {ThreadWork1 work1 = new ThreadWork1 (); Work1.run (); } static void StartT2 () {THREADWORK2 work2 = new ThreadWork2 (); Work2.run (); } static void Main (string[] args) {Task T1 = new Task (() = StartT1 ()); Task t2 = new Task (() = StartT2 ()); _tesTbcollection = new blockingcollection<int> (); Console.WriteLine ("Sample 4-4 Main {"); Console.WriteLine ("Main T1 T2 started {"); T1. Start (); T2. Start (); Console.WriteLine ("Main T1 T2 started}"); Console.WriteLine ("Main wait T1 T2 end {"); Task.waitall (t1, T2); Console.WriteLine ("Main wait T1 T2 End}"); Console.WriteLine ("Sample 4-4 Main}"); Console.readkey (); } }}
Of course, you can try to comment out the completeadding statement in Work1, at which point the WORK2 cannot exit.
Concurrentdictionary is completely lock-free for read operations, and it uses fine-grained locks when many threads are modifying the data.
- AddOrUpdate: If the key does not exist, the method adds a new key and value to the container and, if present, updates the existing key and value.
- Getoradd: If the key does not exist, the method adds a new key and value to the container, returns an existing value if it exists, and does not add a new value.
- TryAdd: Attempt to add a new key and value to the container.
- TryGetValue: Attempts to get a value based on the specified key.
- Tryremove: Attempt to delete the specified key.
- Tryupdate: Conditionally updates the value corresponding to the current key.
- GetEnumerator: Returns an enumerator that can traverse the entire container.
Examples of programs:
Using system;using system.text;using system.threading.tasks;using system.collections.concurrent;namespace Sample4_5 _concurrent_dictionary{class Program {internal static concurrentdictionary<int, int> _testdictionary; Class ThreadWork1//producer {public ThreadWork1 () {} public void run ( {System.Console.WriteLine ("ThreadWork1 run {"); for (int i = 0; i < i++) {System.Console.WriteLine ("ThreadWork1 producer:" + I ); _testdictionary.tryadd (i, I); } System.Console.WriteLine ("ThreadWork1 Run}"); }} class ThreadWork2//consumer {public ThreadWork2 () {} public void run () {int i = 0, ncnt = 0; int nvalue = 0; BOOL IsOk = false; System.Console.WriteLine ("ThreadWork2 run {"); while (Ncnt < +) {IsOk = _testdictionary.trygetvalue (i, out nvalue); if (IsOk) {System.Console.WriteLine ("THREADWORK2 Consumer:" + i * i + " ===== "+ i); Nvalue = Nvalue * Nvalue; _testdictionary.addorupdate (i, Nvalue, (key, value) = = {return value = Nvalue;}); ncnt++; i++; }} System.Console.WriteLine ("ThreadWork2 Run}"); }} static void StartT1 () {ThreadWork1 work1 = new ThreadWork1 (); Work1.run (); } static void StartT2 () {THREADWORK2 work2 = new ThreadWork2 (); Work2.run (); } static void Main (string[] args) {Task T1 = new Task (() = StartT1 ()); Taskt2 = new Task (() = StartT2 ()); bool Bisnext = true; int nvalue = 0; _testdictionary = new Concurrentdictionary<int, int> (); Console.WriteLine ("Sample 4-5 Main {"); Console.WriteLine ("Main T1 T2 started {"); T1. Start (); T2. Start (); Console.WriteLine ("Main T1 T2 started}"); Console.WriteLine ("Main wait T1 T2 end {"); Task.waitall (t1, T2); Console.WriteLine ("Main wait T1 T2 End}"); foreach (var pair in _testdictionary) {Console.WriteLine (pair). Key + ":" + pair. Value); } System.collections.generic.ienumerator<system.collections.generic.keyvaluepair<int, int>> Enumer = _testdictionary.getenumerator (); while (bisnext) {bisnext = Enumer. MoveNext (); Console.WriteLine ("Key:" + Enumer. Current.key + "Value:" + Enumer. Current.value); _testdictionary.tryremove (Enumer. Current.key, out Nvalue); } Console.WriteLine ("\n\ndictionary Count:" + _testdictionary.count); Console.WriteLine ("Sample 4-5 Main}"); Console.readkey (); } }}
Concurrent collections of C # Parallel programming (. Net Framework 4.0)