C#多线程备忘
1.以前一直在用多线程,但对于多个线程完成一个任务时,如何汇合到主线程不太清楚,有时竟傻到去记录每个线程的状态来轮询等待(此处不讨论线程池),下面我写了一个例子,虽然和自己预想的结果有点出入,但确定实现了这个功能,就是Thread.Join方法,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace MultiThreadTest { class Program { static void Main(string[] args) { var thread1 = new Thread(new ThreadStart(ThreadProc1)); var thread2 = new Thread(new ThreadStart(ThreadProc2)); thread1.Start(); thread2.Start(); thread1.Join(); thread2.Join(); Console.WriteLine("{0} All threads end.", DateTime.Now); Console.ReadLine(); } public static void ThreadProc1() { Console.WriteLine("{0} Thread 1 start.", DateTime.Now); Thread.Sleep(5000); Console.WriteLine("{0} Thread 1 end.", DateTime.Now); } public static void ThreadProc2() { Console.WriteLine("{0} Thread 2 start.", DateTime.Now); Thread.Sleep(3000); Console.WriteLine("{0} Thread 2 end.", DateTime.Now); } } }
输出结果如下:
2012-11-30 13:54:41 Thread 1 start. 2012-11-30 13:54:41 Thread 2 start. 2012-11-30 13:54:44 Thread 2 end. 2012-11-30 13:54:46 Thread 1 end. 2012-11-30 13:54:46 All threads end.
2.用Child Task也可以实现这一效果:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace MultiThreadTest { class Program { static void Main(string[] args) { var tasks = new Task(() => { var task1 = new Task(ThreadProc1,TaskCreationOptions.AttachedToParent); var task2 = new Task(ThreadProc2,TaskCreationOptions.AttachedToParent); task1.Start(); task2.Start(); }); tasks.Start(); tasks.Wait(); Console.WriteLine("{0} All threads end.", DateTime.Now); Console.ReadLine(); } public static void ThreadProc1() { Console.WriteLine("{0} Thread 1 start.", DateTime.Now); Thread.Sleep(5000); Console.WriteLine("{0} Thread 1 end.", DateTime.Now); } public static void ThreadProc2() { Console.WriteLine("{0} Thread 2 start.", DateTime.Now); Thread.Sleep(3000); Console.WriteLine("{0} Thread 2 end.", DateTime.Now); } } }
输出结果如下:
2012-11-30 16:27:49 Thread 2 start. 2012-11-30 16:27:49 Thread 1 start. 2012-11-30 16:27:52 Thread 2 end. 2012-11-30 16:27:54 Thread 1 end. 2012-11-30 16:27:54 All threads end.
3.异步线程UI访问时会报错,可以使用委托通过this.invoke来访问:
如果没有参数传递:
1)winform中可以这样访问
this.Invoke(new MethodInvoker(delegate() { //code here })); pictureBox1.Invoke((Action)delegate() { pictureBox1.Update(); });
如果有参数:
private delegate void SetResult(string result); SetResult delSetResult = setResult; this.Invoke(delSetResult, result); //this为UI线程对象
最近发现从c++的com接口回调时,以上方法都无效,直接卡住,应该假死。创建一个新线程代理一下即可解决,
var t = new Thread(new ParameterizedThreadStart(delegate(object p){ var args = p as Form1.ValidateEventReciever.ValidateEventArgs; this.Invoke(new MethodInvoker(delegate() { txtResult.Text = args.EventMessage; })); })); t.Start(e);
this 为当前 form 对象
2)WPF中可以这样访问
public delegate void MethodInvoker(); this.Dispatcher.Invoke(new MethodInvoker(delegate() { btnLogin.IsEnabled = true; }));
4.线程池使用示例。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Diagnostics; namespace ConsoleApplication7 { class Program { private static Random rnd = new Random(); static void Main(string[] args) { var events = new List<ManualResetEvent>(); for (var i = 0; i < 10; i++) { var resetEvent = new ManualResetEvent(false); ThreadPool.QueueUserWorkItem( arg => { doWork(arg); resetEvent.Set(); }, i); events.Add(resetEvent); } WaitHandle.WaitAll(events.ToArray()); Debug.Print("{0}\t All task Completed", DateTime.Now); } public static void doWork(object p) { var id = p.ToString(); var seconds = rnd.Next(1000, 10000); Thread.Sleep(seconds); Debug.Print("{0}\tTask [{1}] Completed", DateTime.Now, id); } } }
输出结果如下:
06/30/2018 10:42:25 Task [3] Completed
06/30/2018 10:42:28 Task [2] Completed
06/30/2018 10:42:29 Task [5] Completed
06/30/2018 10:42:30 Task [1] Completed
06/30/2018 10:42:32 Task [6] Completed
06/30/2018 10:42:32 Task [7] Completed
06/30/2018 10:42:32 Task [4] Completed
06/30/2018 10:42:33 Task [0] Completed
06/30/2018 10:42:34 Task [8] Completed
06/30/2018 10:42:35 Task [9] Completed
06/30/2018 10:42:35 All task Completed
5. 多线程综合运用示例:
class Program { static void Main(string[] args) { demo4(); Console.ReadLine(); } static void demo1() { ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object obj) { for (var i = 0; i < 5; i++) { Console.WriteLine("{0}\ti={1}", DateTime.Now, i); Thread.Sleep(10); } })); ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object obj) { for (var j = 0; j < 5; j++) { Console.WriteLine("{0}\tj={1}", DateTime.Now, j); Thread.Sleep(15); } })); ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object obj) { for (var k = 0; k < 5; k++) { Console.WriteLine("{0}\tk={1}", DateTime.Now, k); Thread.Sleep(13); } })); } static void demo2() { var t1 = new Task(new Action(delegate() { for (var i = 0; i < 5; i++) { Console.WriteLine("{0}\ti={1}", DateTime.Now, i); Thread.Sleep(10); } })); t1.Start(); var t2 = new Task(new Action(delegate() { for (var j = 0; j < 5; j++) { Console.WriteLine("{0}\tj={1}", DateTime.Now, j); Thread.Sleep(15); } })); t2.Start(); var t3 = new Task(new Action(delegate() { for (var k = 0; k < 5; k++) { Console.WriteLine("{0}\tk={1}", DateTime.Now, k); Thread.Sleep(13); } })); t3.Start(); t1.Wait(); t2.Wait(); t3.Wait(); Console.WriteLine("所有任务执行完成"); } static void demo3() { //经测试,子任务必须在父任务内部实例化,否则无法实现任务进度联动 var tasks = new Task(() => { new Task(() => { for (var i = 0; i < 5; i++) { Console.WriteLine("{0}\ti={1}", DateTime.Now, i); Thread.Sleep(10); } }, TaskCreationOptions.AttachedToParent).Start(); new Task(() => { for (var j = 0; j < 5; j++) { Console.WriteLine("{0}\tj={1}", DateTime.Now, j); Thread.Sleep(15); } }, TaskCreationOptions.AttachedToParent).Start(); new Task(() => { for (var k = 0; k < 5; k++) { Console.WriteLine("{0}\tk={1}", DateTime.Now, k); Thread.Sleep(13); } }, TaskCreationOptions.AttachedToParent).Start(); }); tasks.Start(); //异步完成调用 tasks.ContinueWith(parentTask => { Console.WriteLine("所有任务执行完成1"); }); tasks.Wait(); //同步完成调用 Console.WriteLine("所有任务执行完成2"); } static void demo4() { //i,j,k 的三个循环的调用也是随机的,不是按i,j,k的顺序 Parallel.Invoke( () => { //此处的i的输出是并发的,不是从小到大顺序的 Parallel.For(0, 5, i => { Console.WriteLine("{0}\ti={1}", DateTime.Now, i); Thread.Sleep(10); }); }, () => { //此处的j的输出是并发的,不是从小到大顺序的 Parallel.For(0, 5, j => { Console.WriteLine("{0}\tj={1}", DateTime.Now, j); Thread.Sleep(15); }); }, () => { //此处的k的输出是并发的,不是从小到大顺序的 Parallel.For(0, 5, k => { Console.WriteLine("{0}\tk={1}", DateTime.Now, k); Thread.Sleep(13); }); } ); } }