多线程编程学习笔记——线程池(三)_.NET_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > .NET > 多线程编程学习笔记——线程池(三)

多线程编程学习笔记——线程池(三)

 2017/11/20 11:18:22  DotNet菜园  程序员俱乐部  我要评论(0)
  • 摘要:接上文多线程编程学习笔记——线程池(一)接上文多线程编程学习笔记——线程池(二)五、在线程池中使用等待事件处理器与超时本示例主要学习如果对线程池中的操作实现超时,并在线程池中正确等待。线程池还有一个ThreadPool.RegisterWaitForSingleObject,这个方法允许我们将回调函数放入线程池中的队列中。当提供的等待事件处理器接收到信号或发生超时时,这个回调函数将被调用,这样就实现了为线程池中操作实现超时操作。1.代码如下
  • 标签:笔记 学习 多线程 编程 学习笔记 线程

接上文 线程编程学习笔记——线程池(一)

接上文 多线程编程学习笔记——线程池(二)

 

五、     在线程池中使用等待事件处理器超时

     本示例主要学习如果对线程池中的操作实现超时,并在线程池中正确等待。

     线程池还有一个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事件,在此事件中可以知道操作是成功完成,还是发生了错误,或是被取消。

 

上一篇: Winform界面中主从表编辑界面的快速处理 下一篇: 没有下一篇了!
发表评论
用户名: 匿名