大家好,本次讨论的是C#中的并行开发,给力吧,随着并行的概念深入,哥也赶上这个潮流了,其实之前讨论C#的异步调用或者C#中BeginInvoke或者Invoke都已经涉及了部分本篇的内容。

参考书目:Professional.C#.4.0.and.NET.4.pdf 以及 Pro .NET 4 Parallel Programming in C#.pdf

Parallel Program in C#中有Delegate的Asynchronous也有Thread的Asynchronous,前者已经在《C#异步调用详细》中阐述清楚了,那它跟Thread的有什么区别呢?

可能大家都混淆了,我也快糊涂了,C#中异步(并行)编程有几类:

1. Asynchronous Delegates
        Asychronous calling is used when you have work items that should be handled in the background and you care when they finish.

2. BackgroundWorker
        Use BackgroundWorker if you have a single task that runs in the background and needs to interact with the UI. and use it if you don't care when they finish their task. The task of marshalling data and method calls to the UI thread are handled automatically through its event-based model.
        Avoid BackgroundWorker if (1) your assembly does not already reference the System.Windows.Form assembly, (2) you need the thread to be a foreground thread, or (3) you need to manipulate the thread priority.

3. ThreadPool
        Use a ThreadPool thread when efficiency is desired. The ThreadPool helps avoid the overhead associated with creating, starting, and stopping threads.
        Avoid using the ThreadPool if (1) the task runs for the lifetime of your application, (2) you need the thread to be a foreground thread, (3) you need to manipulate the thread priority, or (4) you need the thread to have a fixed identity (aborting, suspending, discovering).

4. Thread class
        Use the Thread class for long-running tasks and when you require features offered by a formal threading model, e.g., choosing between foreground and background threads, tweaking the thread priority, fine-grained control over thread execution, etc.

5. Task Parallel Library 
        Task/TaskFactory, Parallel.For, Parallel.ForEach, Parallel.Invoke

6. Parallel LINQ

TBD

好了进入主题了,先来介绍Thread命名空间。

Thread class

创建与指定委托(Create):
can be constructed with two kinds of delegate:
1. ThreadStart: void ()(void)
2. ParameterizedThreadStart: void ()(Object obj)
跟Asynchronous delegate 相比,输入参数已经很受限制,才支持一个,而且还是object对象的,没有进行类型检查。

控制:
Start() or Start(obj)
Start是直接让新创建的异步执行,类似于Asynchronous的BeginInvoke方法,就是异步于caller来执行。

下面的代码分别显示Asynchronous Delegate以及Thread做同样的事情
Asynchronous Delegate部分

[csharp] view plain copy
  1. public void AsyncOperation(int data)
  2. {
  3. int i = 0;
  4. while (i++ < data)
  5. {
  6. Thread.Sleep(1000);
  7. Console.WriteLine(string.Format("Running for {0} seconds, in thread id: {1}.", i, Thread.CurrentThread.ManagedThreadId));
  8. }
  9. }
  10. public delegate void AsyncOperationDelegate(int data);
  11. public void RunBeginInvoke()
  12. {
  13. AsyncOperationDelegate d1 = new AsyncOperationDelegate(AsyncOperation);
  14. d1.BeginInvoke(3, null, null);
  15. int i = 0;
  16. while (i++ < 3)
  17. {
  18. Thread.Sleep(1000);
  19. Console.WriteLine(string.Format("[BeginInvoke]Running for {0} seconds, in thread id: {1}.", i, Thread.CurrentThread.ManagedThreadId));
  20. }
  21. }

Thread部分:

[csharp] view plain copy
  1. private void AsyncOperation(object obj)
  2. {
  3. int data = (int)obj;
  4. int i = 0;
  5. while (i++ < data)
  6. {
  7. Thread.Sleep(1000);
  8. Console.WriteLine(string.Format("Running for {0} seconds, in thread id: {1}.", i, Thread.CurrentThread.ManagedThreadId));
  9. }
  10. }
  11. public void RunThread()
  12. {
  13. Thread t1 = new Thread(new ParameterizedThreadStart(AsyncOperation));
  14. t1.Start((object)3);
  15. int i = 0;
  16. while (i++ < 3)
  17. {
  18. Thread.Sleep(1000);
  19. Console.WriteLine(string.Format("[Thread]Running for {0} seconds, in thread id: {1}.", i, Thread.CurrentThread.ManagedThreadId));
  20. }
  21. }

使用起来比Asynchronous Delegate实在别扭以及难看。
书中提议使用类的成员函数作为委托函数,同时由于类成员函数能否访问类的成员变量,从而实现复杂或者多个参数传递,或者获取修改后的值。具体例子如下:

[csharp] view plain copy
  1. class ThreadData
  2. {
  3. public int data = 0;
  4. public ThreadData()
  5. {
  6. }
  7. public void RunThread()
  8. {
  9. int i = 0;
  10. while (i++ < 1000000)
  11. data++;
  12. }
  13. }
  14. class ThreadTest
  15. {
  16. blic void RunThreadWithDataInClass()
  17. {
  18. ThreadData d1 = new ThreadData();
  19. Thread t1 = new Thread(d1.RunThread);
  20. t1.Start();
  21. int i = 0;
  22. while (i++ < 1000000)
  23. d1.data++;
  24. Thread.Sleep(2000);// wait for the new thread to finish
  25. Console.WriteLine(string.Format("The data in ThreadData: {0}.", d1.data));
  26. }
  27. }

这样的确能够节省,但是这样就出现了数据竞争的情况,同理也可以使用AD来实现(AD: Asynchronous Delegate)。

后台线程与前台线程的区别:
The process of the application keeps running as long as at least one foreground thread is running. If more
than one foreground thread is running and the Main() method ends, the process of the application remains
active until all foreground threads finish their work.
A thread you create with the Thread class, by default, is a foreground thread. Thread pool threads are
always background threads.
从上面对话来看,AD调用的Thread Pool的线程来执行委托,如果异步委托的宿主也就是caller执行完了, 同时“进程”中没有其他前台线程,则其BeginInvoke的委托将会强制关闭。如果是Thread创建的线程,同时没有修改它成后台线程,则即使caller结束了,ThreadStart或者ParameterizedThreadStart委托将会继续进行。所以这里就出现了必须使用Thread的情况了:当需要创建前台线程时。

IsBackground
ThreadProperty
这两者都是Thread才有的, 而aynchronous delegate没有的

获取返回值
从它的构造函数你觉得它凭什么提供返回值呢,void()() 与 void()(Object obj)来看是没戏的了。不过可以使用成员函数来实现异步调用,也就是说将需要的参数与异步函数内嵌成一个类,像ThreadData类一样,成员变量data来储存异步线程的执行结果,而成员函数RunThread就是需要调用的线程,如果委托(对于ThreadData来说是RunThread函数)费时,则需要在主线程调用Thread的Join函数来实现等待异步线程执行完毕。

至此Thread类就先介绍完毕了。因为后面有一堆后浪出来了,虽然不能说Thread已经死在滩上了,不过也“个头近”了。

蹬蹬蹬蹬, ThreadPool粉墨登场了

其实ThreadPool就是系统已经给你准备好一堆thread等待你的委托,而不用让你管理Thread的细节。
先来看看他有多简单吧:

[csharp] view plain copy
  1. public void ThreadProc(Object state)
  2. {
  3. Thread.Sleep(1000);
  4. Console.WriteLine(string.Format("Running in ThreadProc, id: {0}.", Thread.CurrentThread.ManagedThreadId));
  5. }
  6. public void RunSimpleThreadPool()
  7. {
  8. ThreadPool.QueueUserWorkItem(ThreadProc);
  9. Console.WriteLine(string.Format("Running in main thread, id: {0}", Thread.CurrentThread.ManagedThreadId));
  10. Thread.Sleep(2000);
  11. }

在RunSimpleThreadPool添加2秒睡眠是为了防止主线程结束,后台(ThreadPool里的线程都是后台的)给杀了,所以使用sleep等待一下。
从代码来看,的确很简单。不过他委托的函数的格式更加限制了,必须是符合WaitCallback形式,为void()(Object);
比Thread好不了哪去,之前说类型没有检查的问题,其实使用object作为参数,估计framework的人想给个超超基类指针,的确,object是所有类的老母,所以进去ThreadProc后可以通过GetType来获取类型。

虽然ThreadPool很好用,但是他的限制条件也挺多的,正如上面所列的:
1. 所有线程都是background,也就意味着前台结束了,后台也得跟着倒台。同时它不提供机会给你修改这个属性,不能变前台。
2. 不能设置Priority,想优先不靠谱,还有Name都不能改,挺苦的。
3. For COM objects, all pooled threads are multithreaded apartment (MTA) threads. Many COM objects require a single-threaded apartment (STA) thread。 这个留在后面的篇中讲解,尤其是win form的开发过程中。
4. 放进queue里的委托是不能cancel的。
5. 最后也是个建议,使用ThreadPool执行的委托大多数是短的task, 如果想要生命周期长的,能够提供很多诸如中断,suspend等操作,建议使用Thread。

书上对于ThreadPool的使用也是点到这里,但是作为一名技术狂热者,这些是不够的,来看看使用ThreadPool还能给我们带来什么惊喜吧。
先看RegisterWaitForSingleObject的函数定义:

[csharp] view plain copy
  1. public static RegisteredWaitHandle RegisterWaitForSingleObject(
  2. WaitHandle waitObject,
  3. WaitOrTimerCallback callBack,
  4. Object state,
  5. int millisecondsTimeOutInterval,
  6. bool executeOnlyOnce
  7. )

一个参数就是需要forcus的handle, 第二就是委托函数,类型为WaitOrTimerCallback, 而他需要的函数的格式为 void ()(Object obj, bool timeout),第三个参数就是时间间隔了,最后一个顾名思义,就是是否执行多次,看到这个函数就知道他的作用可以用于执行间隔的查询功能,因为第三个参数,当然啦也可以执行永久的,但是正如前面所说的,生命周期长的线程(不管是前台还是后台)还是选择使用Thread,比较靠谱。

先来看使用这个函数实现间隔执行异步的例子:

[csharp] view plain copy
  1. class ThreadPoolData
  2. {
  3. public string Name = "Default";
  4. public RegisteredWaitHandle Handle = null;
  5. public ThreadPoolData()
  6. {
  7. }
  8. class ThreadPoolTest
  9. {
  10. public void RunRegisterHandle()
  11. {
  12. AutoResetEvent ev = new AutoResetEvent(false);
  13. ThreadPoolData d1 = new ThreadPoolData(){Name = "First"};
  14. d1.Handle = ThreadPool.RegisterWaitForSingleObject(ev, ThreadProc, d1, 1000, false);
  15. Thread.Sleep(3100);
  16. Console.WriteLine("Main thread signals.");
  17. ev.Set();
  18. Console.WriteLine("Main thread finish.");
  19. Thread.Sleep(1000);// wait for the completing the threadproc function
  20. Console.ReadLine();
  21. }
  22. public void ThreadProc(Object state, bool timeout)
  23. {
  24. ThreadPoolData d1 = (ThreadPoolData)state;
  25. string cause = "TIMED OUT";
  26. if (!timeout)
  27. {
  28. cause = "SIGNALED";
  29. if (d1.Handle != null)
  30. d1.Handle.Unregister(null);
  31. }
  32. Console.WriteLine("[0]WaitProc( {0} ) executes on thread {1}; cause = {2}.",
  33. d1.Name,
  34. Thread.CurrentThread.GetHashCode().ToString(),
  35. cause
  36. );
  37. }
  38. }

例子开始定义一个对象(将需要参数塞进类中,然后再传递给异步函数,从而多参数的影响),成员变量有RegisteredWaitHandle,这个是执行RegisterWaitForSingleObject的返回值,这是能否是异步函数能否unregister这个handle的作用,从而从thread queue中撤去,(这个类似Asynchronous Delegate中BeginInvoke的第四个参数经常用其委托函数做参,从而能够实现在callback中调用返回结果; 或者更像Asynchronous Delegate中使用Handle获取返回值一幕,大家可以返回看看,主要使用入参嵌入handle成员,同时在异步函数中调用set,实现主线程结束等待)。

接着是启动函数中调用RegisterWaitForSingleObject的第三个参数是1000,意味着每隔1秒给我跑一次异步函数,注意如果异步函数执行时间超过一秒的话,ThreadPool会选择另一个新的线程执行委托,所以你会看到同一时刻可能有多个相同的委托正在执行,最后一个参数是false表示执行多次,直到对返回值RegisteredWaitHandle执行Unregister。

最后输出结果为:
[0]WaitProc( First ) executes on thread 4; cause = TIMED OUT.
[0]WaitProc( First ) executes on thread 4; cause = TIMED OUT.
[0]WaitProc( First ) executes on thread 4; cause = TIMED OUT.
Main thread signals.
[0]WaitProc( First ) executes on thread 4; cause = SIGNALED.
Main thread finish.

如果将Unregister注释掉了,则输出结果则成为:
[0]WaitProc( First ) executes on thread 4; cause = TIMED OUT.
[0]WaitProc( First ) executes on thread 4; cause = TIMED OUT.
[0]WaitProc( First ) executes on thread 4; cause = TIMED OUT.
Main thread signals.
[0]WaitProc( First ) executes on thread 4; cause = SIGNALED.
Main thread finish.
[0]WaitProc( First ) executes on thread 4; cause = TIMED OUT.
[0]WaitProc( First ) executes on thread 4; cause = TIMED OUT.
[0]WaitProc( First ) executes on thread 4; cause = TIMED OUT.
...repeat n times...
出现的原因是由于:
1. 主线程通过执行Console.ReadLine()来阻塞主线程来等待输入,从而主线程(前台线程)没有掉;
2. 没有执行Unregister函数,ThreadPool就会一直moniter queue中的委托,每次都去执行。

如果将第四个参数改成true,则只执行一次,如果这一秒间隔中没有发生signaled新号,输出结果为:
[0]WaitProc( First ) executes on thread 4; cause = TIMED OUT.
Main thread signals.
Main thread finish.

如果在ThreadProc入口处加入Thread.Sleep(1100), 则输出结果为:
[0]WaitProc( First ) executes on thread 4; cause = TIMED OUT.
Main thread signals.
[0]WaitProc( First ) executes on thread 5; cause = TIMED OUT.
[0]WaitProc( First ) executes on thread 4; cause = TIMED OUT.
Main thread finish.
[0]WaitProc( First ) executes on thread 6; cause = SIGNALED.
从上述结果能看出ThreadPool的确每隔“一秒”执行一次委托,不管委托是否还在跑着。

至此,ThreadPool给我们带来了两个方法: QueueUserWorkItem以及RegisterWaitForSingleObject;前者是快速执行一些类似task的短任务,是可以使用AsynchronousDelegate的BeginInvoke来实现,后者是提供间隔执行委托任务的方法,到目前为止,好像也没有这个介绍,使用情景应该是需要定时查询操作的业务。

接着阐述一下task类,task的技术支持是靠ThreadPool。

先看看task的优势介绍:
you can define continuation work — what should be done after a task is complete. This can be differentiated whether the task was successful or not. Also, you can organize tasks in a hierarchy. For example, a parent task can create new children tasks. This can create a dependency, so that canceling a parent task also cancels its child tasks.

先来看看task能支持的异步函数的签名有Action, Action<Object>,而Action的签名为void()(void)或者void()(Object obj), 跟Thread的委托签名一致。
先看简单的例子:

[csharp] view plain copy
  1. public void ThreadProc(string state)
  2. {
  3. Console.WriteLine("Data : {0}.", state);
  4. }
  5. public void RunTaskWithParameter()
  6. {
  7. string str = "luodingjia";
  8. Task t = new Task((obj) => {
  9. ThreadProc((string)obj);
  10. }, (Object)str);
  11. t.Start();
  12. Console.ReadLine();
  13. }

从这里能够看出lambda的作用的方便。

TaskFactory跟Task 对 THreadPool跟Thread有点异曲同工之妙。
例如使用TaskFactory他也不需要手动的调用类似start的函数,而是直接使用Task.Factory.StartNew(Action)的方法,这跟ThreadPool中的QueueItemWorker很相似,立马执行的。
而Task定义出一个对象之后,还是需要调用Start函数来calling异步委托,这个Thread需要调用Start是一致的。

Task跟TaskFactory都提供类似TaskCreationOptions的枚举量,具体使用的有PreferFairness, LongRunning(内部使用Thread执行),AttachedToParent。

正如之前所说的Task以及TaskFactory有个很强大的功能就是ContinueTask功能,正如下面例子看到的:

[csharp] view plain copy
  1. public void TaskProc()
  2. {
  3. Console.WriteLine("Runing TaskProc in ID: {0}.", Task.CurrentId);
  4. Thread.Sleep(1000);
  5. }
  6. public void TaskProcContinued1(Task t)
  7. {
  8. Console.WriteLine("Runing TaskProcContinued1 in ID: {0} after TaskProc {1}.", Task.CurrentId, t.Status.ToString());
  9. Thread.Sleep(1000);
  10. }
  11. public void TaskProcContinued2(Task t)
  12. {
  13. Console.WriteLine("Runing TaskProcContinued2 in ID: {0} after TaskProc {1}.", Task.CurrentId, t.Status.ToString());
  14. Thread.Sleep(1000);
  15. }
  16. public void TaskProcContinued3(Task t)
  17. {
  18. Console.WriteLine("Runing TaskProcContinued3 in ID: {0} after TaskProcContinued1 {1}.", Task.CurrentId, t.Status.ToString());
  19. Thread.Sleep(1000);
  20. }
  21. public void RunContinueTask()
  22. {
  23. Task t = new Task(TaskProc, TaskCreationOptions.PreferFairness);
  24. Task t1 = t.ContinueWith(TaskProcContinued1);
  25. Task t2 = t.ContinueWith(TaskProcContinued2);
  26. Task t3 = t1.ContinueWith(TaskProcContinued3);
  27. t.Start();
  28. Console.ReadLine();
  29. }

输出结果:
Runing TaskProc in ID: 1.
Runing TaskProcContinued1 in ID: 3 after TaskProc RanToCompletion.
Runing TaskProcContinued2 in ID: 2 after TaskProc RanToCompletion.
Runing TaskProcContinued3 in ID: 4 after TaskProcContinued1 RanToCompletion.

接着讲述Parallel类

针对这个类,主要提供两个方法族,一个是For,另一个是ForEach。从名字就知道他想干嘛的了,就是应用于需要同时开始相同的task的时候就可以通过Parallel来实现。
先一个Parallel的简单的例子:

[csharp] view plain copy
  1. class ParallelData
  2. {
  3. public int Data = 0;
  4. public string Name = "Default";
  5. }
  6. class ParallelTest
  7. {
  8. public ParallelTest()
  9. {
  10. }
  11. public void RunSimpleParallelTest()
  12. {
  13. ParallelLoopResult result = Parallel.For(0, 10, (int val) =>
  14. {
  15. Thread.Sleep(3000);
  16. Console.WriteLine(string.Format("Index: {0}, TaskId: {1}, ThreadId: {2}.", val, Task.CurrentId, Thread.CurrentThread.ManagedThreadId));
  17. });
  18. Console.WriteLine(result.IsCompleted);
  19. }
  20. }

输出结果为:
TaskId: , ThreadId: 1.
Index: 0, TaskId: 1, ThreadId: 1.
Index: 2, TaskId: 2, ThreadId: 3.
Index: 4, TaskId: 3, ThreadId: 4.
Index: 6, TaskId: 4, ThreadId: 5.
Index: 8, TaskId: 5, ThreadId: 6.
Index: 1, TaskId: 6, ThreadId: 7.
Index: 3, TaskId: 7, ThreadId: 8.
Index: 5, TaskId: 1, ThreadId: 1.
Index: 7, TaskId: 8, ThreadId: 3.
Index: 9, TaskId: 9, ThreadId: 4.
True

从上述结果能看出几点:
1. 执行的Index(order)是乱序的,但是能保证[fromInclusive, toExclusive)中的每一个index都平等的执行。
2. 主线程是阻塞于Loop中的,就跟普通的For一样。
3. Parallel的第三个参数(Action<Int32>)中的输入参数就是对应的Index,不太像普通的For循环需要指明迭代变量。

使用ParallelLoopState来监控并且控制整个循环,由于每个Loop都会有自动提供,所以不需要自己创建该类的实例,正如下面例子所示,直接在委托中加入ParallelLoopState pls参数,同时在本体中直接使用:

[csharp] view plain copy
  1. public void RunSimpleParallelTest()
  2. {
  3. DateTime tBgn = DateTime.Now;
  4. ParallelData d1 = new ParallelData() { Data = 5 };
  5. ParallelLoopResult result = Parallel.For(0, 20, (int val, ParallelLoopState pls) =>
  6. {
  7. Thread.Sleep(2000);
  8. d1.Data++;
  9. if (val > 6)
  10. {
  11. pls.Break();
  12. Console.WriteLine(string.Format("Break occurred at {0}, {1}", val, pls.LowestBreakIteration));
  13. }
  14. Console.WriteLine(string.Format("Index: {0}, Data: {1}, TaskId: {2}, ThreadId: {3}.", val, d1.Data, Task.CurrentId, Thread.CurrentThread.ManagedThreadId));
  15. });
  16. Console.WriteLine(result.IsCompleted);
  17. Console.WriteLine(string.Format("LowBreakIteration: {0}.", result.LowestBreakIteration));
  18. DateTime tEnd = DateTime.Now;
  19. TimeSpan ts = tEnd - tBgn;
  20. Console.WriteLine("Using {0} seconds.", ts.TotalMilliseconds);
  21. Console.ReadLine();
  22. }

目的是当所在iteration的index大于6则执行break,中断整个循环, 输出结果为:
Index: 0, Data: 6, TaskId: 1, ThreadId: 9.
Index: 5, Data: 7, TaskId: 2, ThreadId: 10.
Break occurred at 10, 10
Break occurred at 15, 15
Index: 10, Data: 9, TaskId: 3, ThreadId: 11.
Index: 15, Data: 9, TaskId: 4, ThreadId: 12.
Index: 1, Data: 10, TaskId: 5, ThreadId: 13.
Index: 6, Data: 11, TaskId: 6, ThreadId: 14.
Index: 2, Data: 12, TaskId: 1, ThreadId: 9.
Break occurred at 7, 7
Index: 7, Data: 13, TaskId: 7, ThreadId: 10.
Index: 4, Data: 14, TaskId: 8, ThreadId: 11.
Break occurred at 9, 7
Index: 9, Data: 15, TaskId: 9, ThreadId: 14.
Index: 3, Data: 16, TaskId: 1, ThreadId: 9.
False
LowBreakIteration: 7.
Using 6015.625 seconds.
从结果上看,得出以下结论:
Break 可用來與需要執行的目前反覆項目之後沒有其他反覆項目的迴圈進行通訊。 例如,如果從 for 迴圈 (以平行方式從 0 到 1000 逐一查看) 的第 100 個反覆項目呼叫 Break,則所有小於 100 的所有反覆項目仍應執行,但從 101 到 1000 的反覆項目則不一定要執行。
對於已長時間執行的反覆運算,如果目前的索引小於 LowestBreakIteration 的目前值,則 Break 會使 LowestBreakIteration 設定為目前反覆項目的索引。

如果将Break函数改成Stop,输出结果如下:
Break occurred at 10,
Index: 0, Data: 8, TaskId: 1, ThreadId: 1.
Index: 5, Data: 8, TaskId: 2, ThreadId: 3.
Index: 10, Data: 8, TaskId: 3, ThreadId: 4.
False
LowBreakIteration: .
Using 1015.625 seconds.

Stop 可用來與沒有其他反覆項目需要執行的迴圈進行通訊。對於已長時間執行的反覆運算,Stop 會使 IsStopped 針對迴圈的所有其他反覆項目傳回 True,則如果它觀察到 True 值,便會使其他反覆項目檢查 IsStopped 並提早結束。Stop 通常會在以搜尋為基礎的演算法中採用,其中一旦找到位置,則不需要執行其他反覆項目。

上述结论都是从MSDN抄过来,都不太好理解,本人先跳过去了。
接着有类似于Parallel.For的姊妹Parallel.ForEach,后者跟前者很相似,先看例子:

[csharp] view plain copy
  1. public void RunParallelForEach()
  2. {
  3. List<int> intArr = new List<int>();
  4. Random rdn = new Random(DateTime.Now.TimeOfDay.Milliseconds);
  5. for (int i = 0; i < 20; i++)
  6. {
  7. intArr.Add(rdn.Next(100));
  8. }
  9. Parallel.ForEach<int>(intArr, (val, pls, index) => {
  10. Console.WriteLine(string.Format("intArr[{0}]: {1}.", index, val));
  11. });
  12. Console.WriteLine("Finish");
  13. Console.ReadLine();
  14. }

输出结果为:
intArr[0]: 56.
intArr[5]: 87.
intArr[15]: 9.
intArr[16]: 48.
intArr[17]: 27.
intArr[18]: 34.
intArr[19]: 87.
intArr[4]: 9.
intArr[3]: 65.
intArr[11]: 6.
intArr[6]: 44.
intArr[7]: 10.
intArr[13]: 45.
intArr[14]: 77.
intArr[12]: 48.
intArr[10]: 56.
intArr[8]: 72.
intArr[9]: 56.
intArr[1]: 42.
intArr[2]: 16.
Finish

跟For一样,是乱序,Action为第一个参数是容器元素,第二个是parallel的state控制参数,第三个是iteration,也即是容器的索引。

并行编程的同步

1. 使用lock关键字(注意只针对reference type也就是类之类的对象,如果是int就不行了,那是值对象),代码如下:

[csharp] view plain copy
  1. class SynchronousData
  2. {
  3. public int Data = 0;
  4. public string Description = "Default";
  5. }
  6. class SynchronousTest
  7. {
  8. public SynchronousTest()
  9. {
  10. }
  11. private void TaskProc(Object state)
  12. {
  13. SynchronousData d = state as SynchronousData;
  14. if (d != null)
  15. {
  16. int i = 0;
  17. while (i++ < 1000000)
  18. {
  19. <span style="color:#FF0000;">lock(d)</span>
  20. {
  21. d.Data++;
  22. }
  23. }
  24. Console.WriteLine(string.Format("Finish in task id {0}, thread id {1}.", Task.CurrentId, Thread.CurrentThread.ManagedThreadId));
  25. }
  26. }
  27. public void RunSimpleSynchrounousTestInTask()
  28. {
  29. SynchronousData d1 = new SynchronousData() { Data = 5 };
  30. Task t1 = new Task(TaskProc, d1);
  31. Task t2 = new Task(TaskProc, d1);
  32. t1.Start();
  33. t2.Start();
  34. t1.Wait();
  35. t2.Wait();
  36. Console.WriteLine(d1.Data);
  37. Console.ReadLine();
  38. }
  39. }

2. interlocked

使用interlocked是比其他同步方法都要快的,但是能够实现的用途受其成员函数的限制, 对于上述的例子可以使用interlocked.Increment来代替,将

[csharp] view plain copy
  1. lock(d)
  2. {
  3. d.Data++;
  4. }

替换成:

[csharp] view plain copy
  1. Interlocked.Increment(ref d.Data);

3. Moniter

Moniter使用Enter以及Exit函数(类似Mutex一样)来获取有限资源,他跟lock类似,但是他比lock好处是他可以使用TryEnter来指定等待时间,如果等待时间超出了,可以执行别的任务,下面是展示代码:

[csharp] view plain copy
  1. bool lockTaken = false;
  2. Monitor.TryEnter(obj, 500, ref lockTaken);
  3. if (lockTaken)
  4. {
  5. try
  6. {
  7. // acquired the lock
  8. // synchronized region for obj
  9. }
  10. finally
  11. {
  12. Monitor.Exit(obj);
  13. }
  14. }
  15. else
  16. {
  17. // didn't get the lock, do something else
  18. }

C#并行开发_Thread/ThreadPool, Task/TaskFactory, Parallel相关推荐

  1. 那些年我们一起追逐的多线程(Thread、ThreadPool、委托异步调用、Task/TaskFactory、Parallerl、async和await)

    一. 背景 在刚接触开发的头几年里,说实话,根本不考虑多线程的这个问题,貌似那时候脑子里也有没有多线程的这个概念,所有的业务都是一个线程来处理,不考虑性能问题,当然也没有考虑多线程操作一条记录存在的并 ...

  2. C#多线程之Thread,ThreadPool,Task,Parallel

    总目录 文章目录 总目录 前言 一.多线程以及与之相关概念 1.基本概念 1)进程 2)线程 3)多线程 2.同步.异步 1)同步方法 2)异步方法 二.Thread 1.线程的使用 1)创建并开启线 ...

  3. 【转】1.DThread、ThreadPool、Task、Parallel的基本用法、区别以及弊端

    多线程的操作在程序中也是比较常见的,比如开启一个线程执行一些比较耗时的操作(IO操作),而主线程继续执行当前操作,不会造成主线程阻塞.线程又分为前台线程和后台线程,区别是:整个程序必须要运行完前台线程 ...

  4. 8天玩转并行开发——第八天 用VS性能向导解剖你的程序

    8天玩转并行开发--第八天 用VS性能向导解剖你的程序 原文 8天玩转并行开发--第八天 用VS性能向导解剖你的程序 最后一篇,我们来说说vs的"性能向导",通常我们调试程序的性能 ...

  5. 8天玩转并行开发——第三天 plinq的使用

    8天玩转并行开发--第三天 plinq的使用 原文 8天玩转并行开发--第三天 plinq的使用 相信在.net平台下,我们都玩过linq,是的,linq让我们的程序简洁优美,简直玩的是爱不释手,但是 ...

  6. 8天玩转并行开发——第六天 异步编程模型

    原文:8天玩转并行开发--第六天 异步编程模型 在.net里面异步编程模型由来已久,相信大家也知道Begin/End异步模式和事件异步模式,在task出现以后,这些东西都可以被task包装 起来,可能 ...

  7. 并行开发 4.同步机制(上)

    原文:8天玩转并行开发--第四天 同步机制(上) 在并行计算中,不可避免的会碰到多个任务共享变量,实例,集合.虽然task自带了两个方法:task.ContinueWith()和Task.Factor ...

  8. bucket sort sample sort 并行_IBM布局AI硬件大杀器:硬软件并行开发、开源模拟AI工具包...

    原标题:IBM布局AI硬件大杀器:硬软件并行开发.开源模拟AI工具包 智东西(公众号:zhidxcom) 编 | 子佩 智东西11月4日消息,为了解决AI对数据.能源和内存资源的巨大需求,IBM一直致 ...

  9. 并行开发的基本概念及两个重要的定律

    并行开发的基本概念: 同步(Synchronous)和异步(Asynchronous) 同步和异步通常用来形容一次方法的调用.同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为.异 ...

最新文章

  1. vue 输入框限制3位小数_vue+element 中 el-input框 限制 只能输入数字及几位小数(自定义)和输入框之键盘...
  2. 交易性金融资产的账务处理
  3. JS中怎样获取当前年以及前后几年并构造成对象数组
  4. 初识ajaxpro以及使用
  5. 使用Eclipse Deeplearning4j构建简单的神经网络
  6. Direct3D提高篇:HLSL编程实现PhotoShop滤镜效果 - 伪 HDR/Blow
  7. django-用户文件的上传-后台上传
  8. OC正则表达式的使用
  9. 何为量子计算机? | CSDN 博文精选
  10. TensorFlow:判断CUDA和GPU是否可用
  11. ubuntu学习日记--Lesson5:系统目录详解
  12. Ignition Vision基本操作
  13. 现有的DRM数字版权保护技术大全
  14. 使用Git提交代码时出现Author identity unknow
  15. UGUI长按按钮的实现(技能蓄力使用)
  16. 猿创征文|瑞吉外卖——移动端_地址管理
  17. 常用计算机系统包括,常用的保护计算机系统的方法有()。
  18. select2 取值 遍历 设置默认值
  19. 达内培训python百度云下载
  20. 小学计算机考试感受作文,考试后的感想小学作文6篇

热门文章

  1. 腾讯3366小游戏站算法被破解
  2. LFS6.2搭建笔记(一)
  3. Vista光线效果photoshop教程
  4. python的基础知识
  5. WiFi密码分享有妙招 不必口头相传
  6. 基于redis的cas集群配置(转)
  7. (转载博文)VC++API速查
  8. 了解浏览器工作原理-初步
  9. C#算法大全-1-Hanoi
  10. 正交试验优化荔枝乳酸菌饮料的配方