乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      C# 多線程學(xué)習(xí)筆記 - 3

       印度阿三17 2019-02-15

      一、基于事件的異步模式

      1. 基于事件的異步模式 (event-based asynchronous pattern) 提供了簡(jiǎn)單的方式,讓類型提供多線程的能力而不需要顯式啟動(dòng)線程。

        • 協(xié)作取消模型。
        • 工作線程完成時(shí)安全更新 UI 的能力。
        • 轉(zhuǎn)發(fā)異常到完成事件。
      2. EAP 僅是一個(gè)模式,需要開發(fā)人員自己實(shí)現(xiàn)。

      3. EAP 一般會(huì)提供一組成員,在其內(nèi)部管理工作線程,例如 WebClient 類型就使用的 EAP 模式進(jìn)行設(shè)計(jì)。

        // 下載數(shù)據(jù)的同步版本。
        public byte[] DownloadData (Uri address);
        // 下載數(shù)據(jù)的異步版本。
        public void DownloadDataAsync (Uri address);
        // 下載數(shù)據(jù)的異步版本,支持傳入 token 標(biāo)識(shí)任務(wù)。
        public void DownloadDataAsync (Uri address, object userToken);
        // 完成時(shí)候的事件,當(dāng)任務(wù)取消,出現(xiàn)異常或者更新 UI 操作都可以才該事件內(nèi)部進(jìn)行操作。
        public event DownloadDataCompletedEventHandler DownloadDataCompleted;
        
        public void CancelAsync (object userState);  // 取消一個(gè)操作
        public bool IsBusy { get; }                  // 指示是否仍在運(yùn)行
      4. 通過 Task 可以很方便的實(shí)現(xiàn) EAP 模式類似的功能。

      二、BackgroundWorker

      1. BackgroundWorker 是一個(gè)通用的 EAP 實(shí)現(xiàn),提供了下列功能。
        • 協(xié)作取消模型。
        • 工作線程完成時(shí)安全更新 UI 的能力。
        • 轉(zhuǎn)發(fā)異常到完成事件。
        • 報(bào)告工作進(jìn)度的協(xié)議。
      2. BackgroundWorker 使用線程池來創(chuàng)建線程,所以不應(yīng)該在 BackgroundWorker 的線程上調(diào)用 Abort() 方法。

      2.1 使用方法

      1. 實(shí)例化 BackgroundWorker 對(duì)象,并且掛接 DoWork 事件。

      2. 調(diào)用 RunWorkerAsync() 可以傳遞一個(gè) object 參數(shù),以上則是 BackgroundWorker 的最簡(jiǎn)使用方法。

      3. 可以為 BackgroundWorker 對(duì)象掛接 RunWorkerCompleted 事件,在該事件內(nèi)部可以對(duì)工作線程執(zhí)行后的異常與結(jié)果進(jìn)行檢查,并且可以直接在該事件內(nèi)部安全地更新 UI 組件。

      4. 如果需要支持取消功能,則需要將 WorkerSupportsCancellation 屬性置為 true。這樣在 DoWork() 事件當(dāng)中就可通過檢查對(duì)象的 CancellationPending 屬性來確定是否被取消,如果是則將 Cancel 置為 true 并結(jié)束工作事件。

      5. 調(diào)用 CancelAsync 來請(qǐng)求取消。

      6. 開發(fā)人員不一定需要在 CancellationPendingtrue 時(shí)才取消任務(wù),隨時(shí)可以通過將 Cancel 置為 true 來終止任務(wù)。

      7. 如果需要添加工作進(jìn)度報(bào)告,則需要將 WorkerReportsProgress 屬性置為 true,并在 DoWork 事件中周期性地調(diào)用 ReportProcess() 方法來報(bào)告工作進(jìn)度。同時(shí)掛接 ProgressChanged 事件,在其內(nèi)部可以安全地更新 UI 組件,例如設(shè)置進(jìn)度條 Value 值。

      8. 下列代碼即是上述功能的完整實(shí)現(xiàn)。

        class Program
        {
         static void Main()
         {
             var backgroundTest = new BackgroundWorkTest();
             backgroundTest.Run();
             Console.ReadLine();
         }
        }
        
        public class BackgroundWorkTest
        {
         private readonly BackgroundWorker _bw = new BackgroundWorker();
        
         public BackgroundWorkTest()
         {
             // 綁定工作事件
             _bw.DoWork  = BwOnDoWork;
        
             // 綁定工作完成事件
             _bw.WorkerSupportsCancellation = true;
             _bw.RunWorkerCompleted  = BwOnRunWorkerCompleted;
        
             // 綁定工作進(jìn)度更新事件
             _bw.WorkerReportsProgress = true;
             _bw.ProgressChanged  = BwOnProgressChanged;
         }
        
         private void BwOnProgressChanged(object sender, ProgressChangedEventArgs e)
         {
             Console.WriteLine($"當(dāng)前進(jìn)度:{e.ProgressPercentage}%");
         }
        
         private void BwOnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
         {
             if (e.Cancelled)
             {
                 Console.WriteLine("任務(wù)已經(jīng)被取消。");
             }
        
             if (e.Error != null)
             {
                 Console.WriteLine("執(zhí)行任務(wù)的過程中出現(xiàn)了異常。");
             }
        
             // 在當(dāng)前線程可以直接更新 UI 組件的數(shù)據(jù)
        
             Console.WriteLine($"執(zhí)行完成的結(jié)果:{e.Result}");
         }
        
         public void Run()
         {
             _bw.RunWorkerAsync(10);
         }
        
         private void BwOnDoWork(object sender, DoWorkEventArgs e)
         {
             // 這里是工作線程進(jìn)行執(zhí)行的
        
             Console.WriteLine($"需要計(jì)算的數(shù)據(jù)值為:{e.Argument}");
        
             for (int i = 0; i <= 100; i  = 20)
             {
                 if (_bw.CancellationPending)
                 {
                     e.Cancel = true;
                     return;
                 }
        
                 _bw.ReportProgress(i);
             }
        
        
             // 傳遞完成的數(shù)據(jù)給完成事件
             e.Result = 1510;
         }
        }
      9. BackgroundWorker 不是密閉類,用戶可以繼承自 BackgroundWorker 類型,并重寫其 DoWork() 方法以達(dá)到自己的需要。

      三、線程的中斷與中止

      1. 所有 阻塞 方法在解除阻塞的條件沒有滿足,并且其沒有指定超時(shí)時(shí)間的情況下,會(huì)永久阻塞。

      2. 開發(fā)人員可以通過 Thread.Interrupt()Thread.Abort() 方法來解除阻塞。

      3. 在使用線程中斷與中止方法的時(shí)候,應(yīng)該十分謹(jǐn)慎,這可能會(huì)導(dǎo)致一些意想不到的情況發(fā)生。

      4. 為了演示上面所說的概念,可以編寫如下代碼進(jìn)行測(cè)試。

        class Program
        {
         static void Main()
         {
             var test = new ThreadInterrupt();
             test.Run();
             Console.ReadLine();
         }
        }
        
        public class ThreadInterrupt
        {
         public void Run()
         {
             var testThread = new Thread(WorkThread);
        
             testThread.Start();
                // 中斷指定的線程
             testThread.Interrupt();
         }
        
         private void WorkThread()
         {
             try
             {
                 // 永遠(yuǎn)阻塞
                 Thread.Sleep(Timeout.Infinite);
             }
             catch (ThreadInterruptedException e)
             {
                 Console.WriteLine("產(chǎn)生了中斷異常.");
             }
        
             Console.WriteLine("線程執(zhí)行完成.");
         }
        }

      3.1 中斷

      1. 在一個(gè)阻塞線程上調(diào)用 Thread.Interrupt() 方法,會(huì)導(dǎo)致該線程拋出 ThreadInterruptedException 異常,并且強(qiáng)制釋放線程。
      2. 中斷線程時(shí),除非沒有對(duì) ThreadInterruptedException 進(jìn)行處理,否則是不會(huì)導(dǎo)致阻塞線程結(jié)束的。
      3. 隨意中斷一個(gè)線程是十分危險(xiǎn)的,我們可以通過信號(hào)構(gòu)造或者取消構(gòu)造。哪怕是使用 Thread.Abort() 來中止線程,都比中斷線程更加安全。
      4. 因?yàn)殡S意中斷線程會(huì)導(dǎo)致調(diào)用棧上面的任何框架,或者第三方的方法意外接收到中斷。

      3.2 中止

      Thread.Abort() 方法在 .NET Core 當(dāng)中無法使用,調(diào)用該方法會(huì)拋出 Thread abort is not supported on this platform. 錯(cuò)誤。

      1. 在一個(gè)阻塞線程上調(diào)用 Thread.Abort() 方法,效果與中斷相似,但會(huì)拋出一個(gè) ThreadAbortException 異常。
      2. 該異常在 catch 塊結(jié)束之后會(huì)被重新拋出。
      3. 未經(jīng)處理的 ThreadAbortException 是僅有的兩個(gè)不會(huì)導(dǎo)致應(yīng)用程序關(guān)閉的異常之一。
      4. 中止與中斷最大的不同是,中止操作會(huì)立即在執(zhí)行的地方拋出異常。例如中止發(fā)生在 FileStream 的構(gòu)造期間,可能會(huì)導(dǎo)致一個(gè)非托管文件句柄保持打開狀態(tài)導(dǎo)致內(nèi)存泄漏。

      四、安全取消

      1. 與實(shí)現(xiàn)了 EAP 模式的 BackgroundWorker 類型一樣,我們可以通過協(xié)作模式,使用一個(gè)標(biāo)識(shí)來優(yōu)雅地中止線程。

      2. 其核心思路就是封裝一個(gè)取消標(biāo)記,將其傳入到線程當(dāng)中,在線程執(zhí)行時(shí)可以通過這個(gè)取消標(biāo)記來優(yōu)雅中止。

        class Program
        {
         static void Main()
         {
             var test = new CancelTest();
             test.Run();
             Console.ReadLine();
         }
        }
        
        public class CancelToken
        {
         private readonly object _selfLocker = new object();
         private bool _cancelRequest = false;
        
         /// <summary>
         /// 當(dāng)前操作是否已經(jīng)被取消。
         /// </summary>
         public bool IsCancellationRequested
         {
             get
             {
                 lock (_selfLocker)
                 {
                     return _cancelRequest;
                 }
             }
         }
        
         /// <summary>
         /// 取消操作。
         /// </summary>
         public void Cancel()
         {
             lock (_selfLocker)
             {
                 _cancelRequest = true;
             }
         }
        
         /// <summary>
         /// 如果操作已經(jīng)被取消,則拋出異常。
         /// </summary>
         public void ThrowIfCancellationRequested()
         {
             lock (_selfLocker)
             {
                 if (_cancelRequest)
                 {
                     throw new OperationCanceledException("操作被取消.");
                 }
             }
         }
        }
        
        public class CancelTest
        {
         public void Run()
         {
             var cancelToken = new CancelToken();
        
             var workThread = new Thread(() =>
             {
                 try
                 {
                     Work(cancelToken);
                 }
                 catch (OperationCanceledException e)
                 {
                     Console.WriteLine("任務(wù)已經(jīng)被取消。");
                 }
             });
        
             workThread.Start();
        
             Thread.Sleep(1000);
             cancelToken.Cancel();
         }
        
         private void Work(CancelToken token)
         {
             // 模擬耗時(shí)操作
             while (true)
             {
                 token.ThrowIfCancellationRequested();
                 try
                 {
                     RealWork(token);
                 }
                 finally
                 {
                     // 清理資源
                 }
             }
         }
        
         private void RealWork(CancelToken token)
         {
             token.ThrowIfCancellationRequested();
             Console.WriteLine("我是真的在工作...");
         }
        }

      4.1 取消標(biāo)記

      1. 在 .NET 提供了 CancellationTokenSourceCancellationToken 來簡(jiǎn)化取消操作。

      2. 如果需要使用這兩個(gè)類,則只需要實(shí)例化一個(gè) CancellationTokenSource 對(duì)象,并將其 Token 屬性傳遞給支持取消的方法,在需要取消的使用調(diào)用 Source 的 Cancel() 即可。

        // 偽代碼
        var cancelSource = new CancellationTokenSource();
        
        // 啟動(dòng)線程
        new Thread(() => work(cancelSource.Token)).Start();
        
        // Work 方法的定義
        void Work(CancellationToken cancelToken)
        {
            cancelToken.ThrowIfCancellationRequested();
        }
        
        // 需要取消的時(shí)候,調(diào)用 Cancel 方法。
        cancelSource.Cancel();

      五、延遲初始化

      1. 延遲初始化的作用是緩解類型構(gòu)造的開銷,尤其是某個(gè)類型的構(gòu)造開銷很大的時(shí)候可以按需進(jìn)行構(gòu)造。

        // 原始代碼
        public class Foo
        {
            public readonly Expensive Expensive = new Expensive();
        }
        
        public class Expensive
        {
            public Expensive()
            {
                // ... 構(gòu)造開銷極大
            }
        }
        
        // 按需構(gòu)造
        public class LazyFoo
        {
            private Expensive _expensive;
        
            public Expensive Expensive
            {
                get
                {
                    if(_expensive == null) _expensive = new Expensive();
                }
            }
        }
        
        // 按需構(gòu)造的線程安全版本
        public class SafeLazyFoo
        {
            private Expensive _expensive;
            private readonly object _lazyLocker = new object();
        
            public Expensive Expensive
            {
                get
                {
                    lock(_lazyLocker)
                    {
                        if(_expensive == null)
                        {
                            _expensive = new Expensive();
                        }
                    }
                }
            }
        }
      2. 在 .NET 4.0 之后提供了一個(gè) Lazy<T> 類型,可以免去上面復(fù)雜的代碼編寫,并且也實(shí)現(xiàn)了雙重鎖定模式。

      3. 通過在創(chuàng)建 Lazy<T> 實(shí)例時(shí)傳遞不同的 bool 參數(shù)來決定是否創(chuàng)建線程安全的初始化模式,傳遞了 true 則是線程安全的,傳遞了 false 則不是線程安全的。

        public class LazyExpensive
        {
        
        }
        
        public class LazyTest
        {
            // 線程安全版本的延遲初始化對(duì)象。
            private Lazy<LazyExpensive> _lazyExpensive = new Lazy<LazyExpensive>(()=>new LazyExpensive(),true);
        
            public LazyExpensive LazyExpensive => _lazyExpensive.Value;
        }

      5.1 LazyInitializer

      1. LazyInitializer 是一個(gè)靜態(tài)類,基本與 Lazy<T> 相似,但是提供了一系列的靜態(tài)方法,在某些極端情況下可以改善性能。

        public class LazyFactoryTest
        {
         private LazyExpensive _lazyExpensive;
        
         // 雙重鎖定模式。
         public LazyExpensive LazyExpensive
         {
             get
             {
                 LazyInitializer.EnsureInitialized(ref _lazyExpensive, () => new LazyExpensive());
                 return _lazyExpensive;
             }
         }
        
        }
      2. LazyInitializer 提供了一個(gè)競(jìng)爭(zhēng)初始化的版本,這種在多核處理器(線程數(shù)與核心數(shù)相等)的情況下速度比雙重鎖定技術(shù)要快。

        volatile Expensive _expensive;
        public Expensive Expensive
        {
          get
          {
            if (_expensive == null)
            {
              var instance = new Expensive();
              Interlocked.CompareExchange (ref _expensive, instance, null);
            }
            return _expensive;
          }
        }

      六、線程局部存儲(chǔ)

      1. 某些數(shù)據(jù)不適合作為全局遍歷和局部變量,但是在整個(gè)調(diào)用棧當(dāng)中又需要進(jìn)行共享,是與執(zhí)行路徑緊密相關(guān)的。所以這里來說,應(yīng)該是在代碼的執(zhí)行路徑當(dāng)中是全局的,這里就可以通過線程來達(dá)到數(shù)據(jù)隔離的效果。例如線程 A 調(diào)用鏈?zhǔn)沁@樣的 A() -> B() -> C()。

      2. 對(duì)靜態(tài)字段增加 [ThreadStatic] ,這樣每個(gè)線程就會(huì)擁有獨(dú)立的副本,但僅適用于靜態(tài)字段。

        [ThreadStatic] static int _x;
      3. .NET 提供了一個(gè) ThreadLocal<T> 類型可以用于靜態(tài)字段和實(shí)例字段的線程局部存儲(chǔ)。

        // 靜態(tài)字段存儲(chǔ)
        static ThreadLocal<int> _x = new ThreadLocal<int>(() => 3);
        
        // 實(shí)例字段存儲(chǔ)
        var localRandom = new ThreadLocal<Random>(() => new Random());
      4. ThreadLocal<T> 的值是 延遲初始化 的,第一次被使用的時(shí)候 才通過工廠進(jìn)行初始化。

      5. 我們可以使用 Thread 提供的 Thread.GetData()Thread.SetData() 方法來將數(shù)據(jù)存儲(chǔ)在線程數(shù)據(jù)槽當(dāng)中。

      6. 同一個(gè)數(shù)據(jù)槽可以跨線程使用,而且它在不同的線程當(dāng)中數(shù)據(jù)仍然是獨(dú)立的。

      7. 通過 LocalDataStoreSolt 可以構(gòu)建一個(gè)數(shù)據(jù)槽,通過 Thread.GetNamedDataSlot("securityLevel") 來獲得一個(gè)命名槽,可以通過 Thread.FreeNameDataSlot("securityLevel") 來釋放。

      8. 如果不需要命名槽,也可以通過 Thread.AllocateDataSlot() 來獲得一個(gè)匿名槽。

        class Program
        {
         static void Main()
         {
             var test = new ThreadSlotTest();
             test.Run();
             Console.ReadLine();
         }
        }
        
        public class ThreadSlotTest
        {
         // 創(chuàng)建一個(gè)命名槽。
         private LocalDataStoreSlot _localDataStoreSlot = Thread.GetNamedDataSlot("命名槽");
         // 創(chuàng)建一個(gè)匿名槽。
         private LocalDataStoreSlot _anonymousDataStoreSlot = Thread.AllocateDataSlot();
        
         public void Run()
         {
             new Thread(NamedThreadWork).Start();
             new Thread(NamedThreadWork).Start();
        
             new Thread(AnonymousThreadWork).Start();
             new Thread(AnonymousThreadWork).Start();
        
             // 釋放命名槽。
             Thread.FreeNamedDataSlot("命名槽");
         }
        
         // 命名槽測(cè)試。
         private void NamedThreadWork()
         {
             // 設(shè)置命名槽數(shù)據(jù)
             Thread.SetData(_localDataStoreSlot,DateTime.UtcNow.Ticks);
        
             var data = Thread.GetData(_localDataStoreSlot);
             Console.WriteLine($"命名槽數(shù)據(jù):{data}");
        
             ContinueNamedThreadWork();
         }
        
         private void ContinueNamedThreadWork()
         {
             Console.WriteLine($"延續(xù)方法中命名槽的數(shù)據(jù):{Thread.GetData(_localDataStoreSlot)}");
         }
        
         // 匿名槽測(cè)試。
         private void AnonymousThreadWork()
         {
             // 設(shè)置匿名槽數(shù)據(jù)
             Thread.SetData(_anonymousDataStoreSlot,DateTime.UtcNow.Ticks);
        
             var data = Thread.GetData(_anonymousDataStoreSlot);
             Console.WriteLine($"匿名槽數(shù)據(jù):{data}");
        
             ContinueAnonymousThreadWork();
         }
        
         private void ContinueAnonymousThreadWork()
         {
             Console.WriteLine($"延續(xù)方法中匿名槽的數(shù)據(jù):{Thread.GetData(_anonymousDataStoreSlot)}");
         }
        }

      七、定時(shí)器

      7.1 多線程定時(shí)器

      1. 多線程定時(shí)器使用線程池觸發(fā)時(shí)間,也就意味著 Elapsed 事件可能會(huì)在不同線程當(dāng)中觸發(fā)。
      2. System.Threading.Timer 是最簡(jiǎn)單的多線程定時(shí)器,而 System.Timers.Timer 則是對(duì)于該計(jì)時(shí)器的封裝。
      3. 多線程定時(shí)器的精度大概在 10 ~ 20 ms。

      7.2 單線程定時(shí)器

      1. 單線程定時(shí)器依賴于 UI 模型的底層消息循環(huán)機(jī)制,所以其 Tick 事件總是在創(chuàng)建該定時(shí)器的線程觸發(fā)。
      2. 單線程定時(shí)器關(guān)聯(lián)的事件可以安全地操作 UI 組件。
      3. 精度比多線程定時(shí)器更低,而且更容易使 UI 失去響應(yīng)。
      來源:http://www./content-1-114701.html

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多