1. 程式人生 > >C#並發編程

C#並發編程

tran flow logs info slim 執行 方式 dsta 不包含

最近看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#並發編程