多線程編程學習筆記——線程池(三)

来源:http://www.cnblogs.com/chillsrc/archive/2017/11/20/7864929.html
-Advertisement-
Play Games

本示例主要學習如果對線程池中的操作實現超時,併在線程池中正確等待。 線程池還有一個ThreadPool.RegisterWaitForSingleObject,這個方法允許我們將回調函數放入線程池中的隊列中。當提供的等待事件處理器接收到信號或發生超時時,這個回調函數將被調用,這樣就實現了... ...


接上文 多線程編程學習筆記——線程池(一)

接上文 多線程編程學習筆記——線程池(二)

 

五、     線上程池中使用等待事件處理器與超時

     本示例主要學習如果對線程池中的操作實現超時,併在線程池中正確等待。

     線程池還有一個ThreadPool.RegisterWaitForSingleObject,這個方法允許我們將回調函數放入線程池中的隊列中。當提供的等待事件處理器接收到信號或發生超時時,這個回調函數將被調用,這樣就實現了為線程池中操作實現超時操作。

1.代碼如下:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
namespace ThreadTPLDemo
{

    class Program
    {   

        static void Main(string[] args)
        {
            Console.WriteLine("開始測試線程池中定時運行操作。。。");
            TimesOperation(TimeSpan.FromSeconds(5));//提供了5秒的操作時間,會超時
            TimesOperation(TimeSpan.FromSeconds(9));//提供了9秒的操作時間,正常工作
            Console.WriteLine("。。。。。。。。。。。。。。。。");
            Console.Read();
        }

        private static void  TimesOperation(TimeSpan  workTimes)
        {
            using (var manuEvt=new ManualResetEvent(false))
            {
                using (var cts=new CancellationTokenSource())
                {
                    Console.WriteLine("開始--線程池中的定時操作。。。");
                    var work = ThreadPool.RegisterWaitForSingleObject
                        (manuEvt, (state, isTimeOut) => AsyncOperWait(cts, isTimeOut), null, workTimes, true);
                    Console.WriteLine("一個長時間運行的線程操作。。。");

                    ThreadPool.QueueUserWorkItem(_ => AsyncOper(cts.Token, manuEvt));
                    Console.WriteLine("。。。間隔2秒再次運行。。。");                    Thread.Sleep(workTimes.Add(TimeSpan.FromSeconds(2)));
                    work.Unregister(manuEvt);
                }
            } 
        }
        private static void AsyncOper(CancellationToken token,ManualResetEvent mrevt)
        {
            Console.WriteLine("開始--線程池中的第一個工作線程。。。");
            for (int i = 0; i < 7; i++)
            {
                if (token.IsCancellationRequested)//判斷是否已經取消操作
                {
                    Console.WriteLine("使用輪詢方法取消工作線程  ID:{0}", Thread.CurrentThread.ManagedThreadId);
                    return;
                }
                Thread.Sleep(TimeSpan.FromSeconds(1));
            }

            mrevt.Set();
            Console.WriteLine("-------線程池中的第一個工作線程 發出信號----------");
        }
        private static void AsyncOperWait(CancellationTokenSource cts, bool isTimeOut)
        {
            Console.WriteLine("開始--線程池中的第二個工作線程。。。");

            if (isTimeOut)//判斷是否已經取消操作
            {
                cts.Cancel();
                Console.WriteLine("工作線程已經超時,並取消。  ID:{0}", Thread.CurrentThread.ManagedThreadId);
            }
            else
            {

                Console.WriteLine("-------線程池中的第二個工作線程 工作完成----------");
            }                


        }
    }
}

 

 2.程式結果如下。

       程式啟動之後按順序放入了一些長時間運行的操作,這個操作運行6秒,如果運行成功,則會設置一個ManualResetEvent信號。如果取消了這個操作,則這個操作會被丟棄。

       我們還註冊了第二個非同步操作,當從ManualResetEvent對象中接受了一個信號之後,這個非同步操作會被調用。如果第一個操作順利執行,則會設置信號。如果第一個操作執行超時,則會通過CancellationToken來取消第一個操作。

 註:當線程池中大量的操作被阻塞時,上面的方法就非常有用了。

 

六、     使用計時器

     使用Threading.Timer對象實現線程池中的周期性調用的非同步操作。

 1.代碼如下:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading; 

namespace ThreadPoolDemo
{  

    class Program
    {
        static Timer timer;  

        static void Main(string[] args)
        {
            Console.WriteLine("開始測試線程池中通過計時器運行操作,輸入A停止計時器。。。");
            DateTime startTime = DateTime.Now;
            timer = new Timer(_=>TimesOperation(startTime),null,TimeSpan.FromSeconds(1),TimeSpan.FromSeconds(2));
            Thread.Sleep(6000);
            timer.Change(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(4)); 

            Console.WriteLine("。。。。。。。。。。。。。。。。");
            ConsoleKeyInfo key = Console.ReadKey();
            if (key.Key==ConsoleKey.A)
            {
                timer.Dispose();
            }
            Console.Read();
        }

        private static void TimesOperation(DateTime startTime)
        {
            TimeSpan time = DateTime.Now - startTime;
            Console.WriteLine("線程 {0} 從 {1} 開始 運行了 {2} 秒",
            Thread.CurrentThread.ManagedThreadId, startTime.ToString("yyyy-MM-dd HH:mm:ss"), time.Seconds); 

        }
    }
}

 

2.程式運行結果如下。

 

        程式啟動時,首先創建了一個timer,第一個參數是lambla表達式,將會線上程池中執行,第二個參數是null。然後調用TimerOperation方法,並給一個初始時間,並指定什麼時候會第一次運行TimerOperation,以及之後的再次調用間隔時間。

七、     使用BackgroundWorker組件

       本示例使用BackgroundWorker組件實現非同步操作。

       程式啟動時創建了一個BackgroundWorker對象實例,顯示的指示這個後臺工作線程支持取消操作及操作進度通知。

 1.代碼如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
namespace ThreadTPLDemo
{ 

    class Program
    {
        static Timer timer;   

        static void Main(string[] args)
        {
            Console.WriteLine("開始測試 BackgroundWorker。。。");
            BackgroundWorker bgwork = new BackgroundWorker();
            bgwork.WorkerReportsProgress = true;
            bgwork.WorkerSupportsCancellation = true; 

            bgwork.DoWork += worker_dowork;
            bgwork.ProgressChanged += worker_ProgressChanged;
            bgwork.RunWorkerCompleted += worker_Completed; 

            bgwork.RunWorkerAsync();//開始後臺運行        

            Console.WriteLine("。。。。。輸入C取消BackgroundWorker後臺組件。。。。。。。。。。。");
            do
            {
                ConsoleKeyInfo key = Console.ReadKey();
                if (key.KeyChar.ToString().ToUpper() == "C")
                {
                    bgwork.CancelAsync();
                }
            } while (bgwork.IsBusy);       

            Console.Read(); 

        }
        static void worker_dowork(object sender,DoWorkEventArgs e)
        {           
            Console.WriteLine("線程 {0}  開始 運行",
            Thread.CurrentThread.ManagedThreadId);
            int result = 0;
            var bgwork = (BackgroundWorker)sender;
            for (int i = 0; i < 100; i++)
            {
                if (bgwork.CancellationPending)//已經取消後臺操作
                {
                    e.Cancel = true;
                    return;
                }

                if (i%10==0)
                {
                    bgwork.ReportProgress(i);//顯示進度
                }
                Thread.Sleep(200);
                result += i;
            }

            e.Result = result;
        }
        static void worker_ProgressChanged(object sender,ProgressChangedEventArgs e)
        {
            Console.WriteLine("線程 {0}   已經完成工作量的 {1} %",          Thread.CurrentThread.ManagedThreadId,e.ProgressPercentage);
        }

        static void worker_Completed(object sender,RunWorkerCompletedEventArgs e)
        {
            Console.WriteLine("線程 {0} 已經執行結束!",
        Thread.CurrentThread.ManagedThreadId);
            if (e.Error!=null)
            {
                Console.WriteLine("線程 {0} 發生錯誤,錯誤信息:{1}",
     Thread.CurrentThread.ManagedThreadId,e.Error.Message);
            }
            else if (e.Cancelled)
            {
                Console.WriteLine("線程 {0} 已經取消",
   Thread.CurrentThread.ManagedThreadId);
            }
            else
            {
                Console.WriteLine("線程 {0} 執行成功,結果是:{1}",
   Thread.CurrentThread.ManagedThreadId, e.Result);
            }
        }
    }
}

2.程式正常執行結束,如下圖。

 

3.  程式執行的中途,人工干預,取消。如下圖。請看下圖中黃框的位置,我輸入了字母C,則線程被取消。

 

 在程式中我們定義了三個事件。

  1. DoWork事件,當一個後臺工作對象通過RunWorkerAsync啟動一個非同步操作時,將調用這個事件處理器。這個事件處理器會運行線上程池中,當運行結束,將運行結果做為參數傳遞給RunWorkerCompleted事件,同時觸發此事件。
  2. ProgressChanged事件,通過接收BackgroundWorker的ReportProgress方法傳遞過來的參數,顯示線程執行的進度。
  3. RunWorkerCompleted事件,在此事件中可以知道操作是成功完成,還是發生了錯誤,或是被取消。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 我們在(人工智慧搶80萬工人的飯碗,卻增加了350萬個新崗位?!)文章中概述了人工智慧帶來的便利之處,那麼越來越多的人對人工智慧趨之若鶩,那人工智慧的就業崗位如何呢?薪資待遇如何呢?又有哪些實力強大的AI公司呢? 最課程為大家分析各個招聘網站,python在智聯招聘上薪資待遇4k-50k不等;拉勾網 ...
  • cobbler-自動安裝系統 1.1 cobber簡介 1.1.1 cobbler說明 1.2 服務端部署 1.2.1 系統環境 系統:CentOS 7.4 ip: 外網:10.0.0.202 內網:172.16.1.202 1.2.2 安裝依賴包 1.2.3 修改配置文件 #備份、修改cobble ...
  • Windows常用的分區格式有三種,分別是FAT16、FAT32、NTFS格式。 在Linux操作系統里有Ext2、Ext3、Linux swap和VFAT四種格式。 FAT16: 作為一種文件名稱,FAT(File Allocation Table,文件分配表)自1981年問世以來,已經成為一個計 ...
  • In Doing We Learning 在操作中學習。如果只是光看教程,沒有實際的操作,對編程語言的理解很空泛,所以決定從單片機中學習C語言。 #include<reg52.h> //包含的頭文件。 sbit LED = P0^0; //這裡就是對P0寄存器進行位操作。相當於對P0^0位賦予了一個 ...
  • 會話保持的三種方式 Session sticky會話綁定:通過在前端調度器的配置中實現統一session發送至同一後髮端伺服器 Session cluster會話集群:通過配置Tomcat保持所有Tomcat的session的信息一致。 Session server 會話服務:將所有的session ...
  • 1》相關知識簡介: 1>常用的磁碟IO調度器: CFQ:完全公平隊列演算法; deadline:最後期限演算法; anticipatory:順序讀寫隊列演算法/預期演算法; NOOP:no operation,最簡單的調度演算法; 2>如何替換IO調度演算法:預設演算法為CFQ; /sys/block/<devi ...
  • kickstart 部署 - 無人值守安裝系統(linux) ...
  • 1.安裝nodejs,自帶npm環境。 地址:https://nodejs.org/en/download/,Node.js 歷史版本下載地址:https://nodejs.org/dist/ 可自行選擇適合自己操作系統的版本。 安裝過程參見:http://www.runoob.com/nodejs ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...