C#并发编程
最近看C# 并发编程··,这里做一下总结··多线程,异步,并行,大部分都是最近看C#并发编程这个书涉及到的··这里仅仅列出实现方式,书里边介绍的其他的东西没有涉及比如取消操作,同步操作,集合之类的东西
线程:Thread,ThreadPool,BackgroundWorker,
Thread 可以又更多控制··ThreadPool就是丢进去系统好管理线程,BackgroundWorker相当于加了事件的线程,用在thread执行函数里边加事件,外边注册加invoke就可以实现类似backgroundworker的功能,
但是机制好像不太一样,看了反编译的方法invoke里边的代码 带了大量的非托管代码··看不大懂··,backgroundworker内部使用委托的异步执行方式,就是begininvoke, C#的begininvoke内部实现好像就是多线程,通过同步上下文回到ui线程实现类似invoke 的操作
thread 和threadpool都可以传递一个委托进去,这样可以通过委托做一些特殊操作··也可以直接定义个事件实现通知进度的功能
4.5的async/await 里边iprogress也能实现类似报告进度功能
代码在winform窗体里边执行
private event EventHandler myevent;
//thread myevent += delegate { Invoke(new EventHandler(delegate { Text = "threadtest"; })); }; Action myaction = new Action(() => { MessageBox.Show("delegatetest"); }); new Thread(new ParameterizedThreadStart((obj) => { //dosomthing Thread.Sleep(2000); (obj as Action)(); })).Start(myaction); new Thread(new ThreadStart(() => { //dosomething Thread.Sleep(2000); myevent?.Invoke(null, new EventArgs()); })).Start(); //backgroundworker BackgroundWorker worker = new BackgroundWorker(); worker.WorkerReportsProgress = true; worker.DoWork += delegate { //dosomething Thread.Sleep(2000); worker.ReportProgress(100); }; worker.ProgressChanged += (send, stat) => { Text = "doworker"; }; worker.RunWorkerAsync(); //threadpool ThreadPool.QueueUserWorkItem(new WaitCallback((stat) => { //dosomthing Thread.Sleep(1000); })); //task 异步 Task t = new Task(() => { //dosomething Thread.Sleep(3000); }); Action<Task> taction = new Action<Task>((tas) => { Text = "Task"; }); var context = TaskScheduler.FromCurrentSynchronizationContext();//这里创建一个当前上下文的任务调度器,其实就是当前的ui线程上下文 t.ContinueWith(taction, context);//吧上边的调度器传入,接着的这个任务就会用这个调度器执行,内部其实就是post方法把操作放在当前ui线程进行同步执行,这样就不会报错了 t.Start();//这里是异步的方式,默认是以线程池的方式执行,如果在这里放入ui操作会报错线程间操作无效
之前的异步编程,通过beginInvoke ,委托和control都有类似方法,invoke就是同步执行, begininvoke异步执行,这个里边据说也是用线程池实现的异步
Action act = new Action(() => { Thread.Sleep(2000); Console.WriteLine("121231"); }); var callback = new AsyncCallback((iasynccallback) => { Console.WriteLine("OK"); }); var res=act.BeginInvoke(callback, null); Console.WriteLine("异步测试"); act.EndInvoke(res);
上边有Task的写法,task通过任务调度器也就是TaskScheduler来实现调度,可以在当前线程执行,也可以通过线程池执行,这个TaskScheduler 有两种实现,一种是用线程池实现,一种用上下文实现类似上边的backgroundworker,
也就是SynchronizationContext这个类,这个类又一个Post方法可以将异步的方法以同步的方式执行到指定的上下文中去,
而4.5里边的async/await也是以类似的方式,又上下文这个概念,这个async/await 花样可多了···这里就不多说了。。。
这里举个例子,可以把窗体时间直接定义为异步函数,这样事件方法里边用await去异步执行,而在方法中又可以直接刷新ui,下边的代码是可以运行成功的· 这个完全颠覆了之前的写法··之前的如果要进行异步,比如线程中要更新ui就要invoke否则肯定要报错,或者使用上面委托的异步执行通过回调函数的方式去执行ui刷新应该也是要invoke的
btn.Click += Displaywebstringlength; async void Displaywebstringlength(object sender,EventArgs e) { label.Text = "Fethcing"; using (HttpClient client = new HttpClient()) { string text =await client.GetStringAsync(@"https://www.baidu.com/"); label.Text = text.Length.ToString(); } }
并行·
当有大量的不相干的事的集合要进行操作就可以用并行了也就是Parallel,这玩意内部也是用task实现的但是实现写好复杂 没看明白·····总之碉堡了···
也可以用对应的linq实现PLINQ,
建议使用Parallel 这个会根据cpu状态动态调整,而plinq没有这个考虑
比如我有一堆文件要读取,就可以这样读,或者我要检查局域网的那些IP能ping同,或者做一些数字的聚合操作
var nums = Enumerable.Range(1, 20); Parallel.ForEach(nums, new Action<int>(n => Console.WriteLine(n)));//Parallel的实现 nums.AsParallel().ForAll(n => Console.WriteLine(n));//Plinq的实现
//任务并行执行
Action maction = () => {
Console.WriteLine("ParelTest");
};
Parallel.Invoke(maction);
TPL数据流,就是把事件弄的想流一样执行·· 我实在没搞明白这玩意又啥用 反正很流弊l啦,这个东西需要用nuget下一个微软的库,这个库是额外,也就是不包含在fcl中,Microsoft.Tpl.Dataflow
static async Task Test() { //string uri = @"https://www.baidu.com/"; //string res = await DownloadWrithRitries(uri); //Console.WriteLine(res); var multiplyBlock = new TransformBlock<int, int>(item => { item = item * 2; Console.WriteLine(item); return item; }); var substractblock = new TransformBlock<int, int>(item => { item = item - 2; Console.WriteLine(item); return item; }); var opions = new DataflowLinkOptions { PropagateCompletion = true }; multiplyBlock.LinkTo(substractblock, opions); multiplyBlock.AsObserver().OnNext(20); multiplyBlock.Complete(); await substractblock.Completion; }
Rx这个也是要通过nuget安装Rx-Main,这玩意是基于IObservable<T>也就是观察者模式·的玩意··这里不做解释了···主要是我的nuget没下到这玩意·····
这里这是列举了这些异步的方式· 应该都支持取消操作,类似CancellationTokenSource这个类型的东西·
所以书里边推荐使用Task和async/await,
然后还有涉及同步方式的问题主要是阻塞锁,异步锁SemaphoreSlim(其实是限流),阻塞信号
或者使用线程安全集合比如ConcurrentBag,ConcurrentDictionary分别对应列表和字典的线程安全集合,类似的还有栈和队列以及set的实现,这玩意内部实现好像就是monitor和innerlock配合,说是效率还可以·
·贴个代码··这个是反编译的ConcurrentBag的添加操作代码
private void AddInternal(ConcurrentBag<T>.ThreadLocalList list, T item) { bool flag = false; try { Interlocked.Exchange(ref list.m_currentOp, 1); if (list.Count < 2 || this.m_needSync) { list.m_currentOp = 0; Monitor.Enter(list, ref flag); } list.Add(item, flag); } finally { list.m_currentOp = 0; if (flag) { Monitor.Exit(list); } } }
另外微软还有一个不可变集合库,这玩意需要去nuget下载,都是以Immutable开头的··
所谓的不可变意思是每次操作都返回一个全新的集合,在api实现的时候集合里边实现的存储共享···具体怎么实现就不知道了
var stack = ImmutableStack<int>.Empty; stack = stack.Push(13); var biggerstack = stack.Push(7); foreach (var item in stack) Console.WriteLine(item); foreach (var item in biggerstack) Console.WriteLine(item); //两个栈共享了存储项目13的内存