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的實現
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的內存
C#並發編程