c#专题——多线程处理

149 篇文章 148 订阅

一、 Thread
使用Thread执行异步操作

  private void DoWork()
        {
            for (int i = 0; i < 1000; i++)
            {
                Console.Write('+');
            }
        }

调用:

 ThreadStart threadStart = DoWork;
            Thread thread = new Thread(threadStart);
            thread.Start();
            for (int i = 0; i < 1000; i++)
            {
                Console.Write('-');
            }

            thread.Join();//让ui主线程等待thread线程执行完毕再继续执行
            Console.WriteLine("主线程结束");

输出

-----------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++-----------------------------------------------
-------------------------------------------------------------------------------------------
--------------------------------------------------+++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------
-------------------------------------------------------------------------------------------
------------------------------------------------------------------+++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------
-------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-------------------------------------------------------------------------------------------
--------------------------------------------------------+++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++主线程结束

结论:
1)以上的输出虽然是直接输出到控制台,但是不是打开vs,然后从vs里面的输出窗口看到的,因为vs的输出窗口中输出的内容只有一行,不方便我们对测试结果进行分析,本次输出的内容是直接运行.exe执行程序,然后在弹出的黑窗口中看到的(先弹出黑窗口,然后执行exe),关于黑窗口的用法,参考该博文:winform中调用console窗口

2)从上面也可以看出虽然UI线程和thread 是两个并行执行的线程,但是由于thread线程创建需要时间,所以UI线程的方法首先执行,但是两个线程的内容是交替输出的,也就是线程之间发生了上下文切换。

3)“主线程结束”,这几个字的信息是最后输出的,因为 thread.Join()让当前线程等待thread线程执行完成,这里的当前线程就是UI线程,所以 Console.Write(’-’)执行完成以后,没有直接输出“主线程结束”,而是等Console.Write(’+’)方法执行完成以后再输出。

再来看Thread类的几个用法
1、给Thread类的对象传入参数
上面的例子中,我们使用了thread的一个重载public Thread(ThreadStart start),因为ThreadStart是一个无参的委托,所以当实例化Thread类时没有办法传入参数,这时我们可以使用thread的另一个重载public Thread( ParameterizedThreadStart start),ParameterizedThreadStart是一个参数类型为object类型的委托,用法如下:

  private void DoWork(object  str)
        {
            for (int i = 0; i < 1000; i++)
            {
                Console.Write(string.Concat ( str.ToString (),'+'));
            }
        }
 ParameterizedThreadStart threadStart = DoWork;
            Thread thread = new Thread(threadStart);
            thread.Start(":");
            for (int i = 0; i < 1000; i++)
            {
                Console.Write('-');
            }

            thread.Join();//让ui主线程等待thread线程执行完毕再继续执行
            Console.WriteLine("主线程结束");

输出

--------------------------------------:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+
:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+
:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+
:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+----------------------------------
------------------------------------------------------------------------------------------------------------------------
---:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:
+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:
+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+---------
----------------------------:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+
:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+
:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+
:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+----------------------------
------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+
:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+----------------------------------------------------------------
-----------------------------:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:
+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:
+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+-------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------
-----:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:
+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+-----------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------:+:+:+:+:+:+:+:+:+:+:+:
+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:
+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:+:
+:+:+:+:+:+:+:+:+:+:+---------------------------------------------------------------------------------------------------
主线程结束

从上面输出的结果看,参数":"成功的被传入方法中。

2、指定线程为后台线程

 ParameterizedThreadStart threadStart = DoWork;
            Thread thread = new Thread(threadStart);
            thread.Start(":");
 private void DoWork(object  str)
        {
            while(true)
            {
                for (int i = 0; i < 1000; i++)
                {
                    Console.Write(string.Concat(str.ToString(), '+'));
                }
                Thread.Sleep(5);
            }
        }

当我们没有将thread 设置为后台线程的话,我们会发现,当我们关闭窗体后,黑窗体依旧在打印信息,这是因为我们只关闭了ui所在的前台线程,但是没有关闭thread 前台线程,但是进程只有在所有的前台线程关闭的情况下才能关闭,所以进程没有被关闭,打印照样继续,为了解决这个问题,我们需要将thread 的IsBackground属性设置为true,如下:

 ParameterizedThreadStart threadStart = DoWork;
        Thread thread = new Thread(threadStart) { IsBackground=true};//初始化的时候给属性设值
        thread.Start(":");

这样,当我们关闭窗体的时候,整个应用程序就关闭了,当然也可以在主窗体的formclosing事件中杀死进程,这时就不需要指定线程为后台线程。
3、指定线程的优先级
msdn给出的例子如下:

 PriorityTest priorityTest = new PriorityTest();

            Thread thread1 = new Thread(priorityTest.ThreadMethod);
            thread1.Name = "ThreadOne";
            thread1.Priority = ThreadPriority.Highest ;
            Thread thread2 = new Thread(priorityTest.ThreadMethod);
            thread2.Name = "ThreadTwo";
            thread2.Priority = ThreadPriority.BelowNormal;
            Thread thread3 = new Thread(priorityTest.ThreadMethod);
            thread3.Name = "ThreadThree";
            thread3.Priority = ThreadPriority.AboveNormal;

            thread1.Start();
            thread2.Start();
            thread3.Start();
            // Allow counting for 10 seconds.
            Thread.Sleep(10000);
            priorityTest.LoopSwitch = false;
 class PriorityTest
    {
        static bool loopSwitch;
        [ThreadStatic] static long threadCount = 0;

        public PriorityTest()
        {
            loopSwitch = true;
        }

        public bool LoopSwitch
        {
            set { loopSwitch = value; }
        }

        public void ThreadMethod()
        {
            //        long threadCount = 0;

            while (loopSwitch)
            {
                threadCount++;
            }
            Console.WriteLine("{0,-11} with {1,11} priority " +
                "has a count = {2,13}", Thread.CurrentThread.Name,
                Thread.CurrentThread.Priority.ToString(),
                threadCount.ToString("N0"));
        }
    }

输出:

ThreadTwo   with BelowNormal priority has a count = 1,738,832,612
线程 0x506c 已退出,返回值为 0 (0x0)。
ThreadThree with AboveNormal priority has a count = 1,733,781,710
ThreadOne   with     Highest priority has a count = 1,747,720,585

理论上,线程优先级高的执行的概率大,但是实际执行的结果并不是这样,ThreadTwo优先级最低,但是却比ThreadThree执行的多。
所以我们需要记住以下两点:
1)优先级高的不一定优先执行,优先级低的也有可能先执行,只是优先级高的执行的概率大一点;
2)两个优先级相差越大,则按照设定优先级执行的概率越大,所以如果想让一个线程先执行,可以设置为Highest,另一个线程优先级设置为Lowest就好。
二、ThreadPool
由于线程是昂贵的资源,线程之间的上下文切换耗费时间,如果我们在使用多线程时,创建和销毁线程都是耗时的,线程池不会销毁线程,当新的工作来临的时候省去了重新创建线程耗费的时间,而且避免了创建多个线程来完成工作。

  private void DoWork(object obj)
        {
            for (int i = 0; i < 100; i++)
            {
                Console.WriteLine('+');
            }
        }
  ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork)); ;
        for (int i = 0; i < 100; i++)
        {
            Console.WriteLine('-');
        }

        Thread.Sleep(1000);
        Console.WriteLine("主线程2结束");

三、Task
参考了很多资料得出的结论是:Task性能在多cpu下强于ThreadPool,单个cpu下和ThreadPool差不多,用法如下:
1、

 Task  task = Task.Run(new Action(test));
  private void test()
        {
            while (true )
            { 
                Console.WriteLine("测试");
            }
        }

2、

 Task task1 = Task.Factory.StartNew(new Action (test));

以上两种方法相同。
3、任务延续

  var parent0 = Task.Run(()=> { Console.WriteLine("task0Start"); } );
        var parent00 = parent0.ContinueWith((obj)=> { Console.WriteLine("task00Continue"); }, TaskContinuationOptions.None);//延续任务在先驱任务parent0执行完毕后再异步执行,TaskContinuationOptions.None是说明无论先驱任务执行完毕后是什么状态,延续任务都执行。


4、取消Task
1)立即取消

CancellationTokenSource tokenSource = new CancellationTokenSource();

            // create the cancellation token
            CancellationToken token = tokenSource.Token;//标记
         
            Task task = Task.Run(() =>
            {
                for (int i = 0; i < 10000; i++)
                {
                    Console.WriteLine(i);
                    if(token .IsCancellationRequested )//for循环类似轮询取消状态
                    {
                        break;
                    }
                }
            });
            Thread.Sleep(50);
            tokenSource.Cancel();//取消task

2)等待指定的时间后再取消

  CancellationTokenSource tokenSource = new CancellationTokenSource();

            // create the cancellation token
            CancellationToken token = tokenSource.Token;//标记

            Task task = Task.Run(() =>
            {
                for (int i = 0; i < 10000000; i++)
                {
                    Console.WriteLine(i);
                    if (token.IsCancellationRequested)//for循环类似轮询取消状态
                    {
                        break;
                    }
                }
            });
            tokenSource.CancelAfter(2000);//等待两秒后再取消task

5、基于任务的异步模式
async和await的一个使用例子

 async Task<string> ReadFileAsync()//async将方法修饰为异步方法
        {
             Console.WriteLine("开始执行异步方法读取文件");
            Thread.Sleep(1000);
            Console.WriteLine("开始执行等待异步方法读取文件");
            await Task.Delay(1000);//程序直接返回到调用异步方法的主程序
            return "文件1";
        }
        
  private async  void button1_Click(object sender, EventArgs e)
        {
            Task<string> str = ReadFileAsync();
            Console.WriteLine("在读取文件期间做了别的事情");
            string result = await str;
            Console.WriteLine(result);
        }

输出

开始执行异步方法读取文件
开始执行等待异步方法读取文件
在读取文件期间做了别的事情
文件1

1)async
async指示方法是异步的,并且该方法的返回类型有三种Task、 Task或void的
上面的代码async修饰了Task以及void,下面举例说明async修饰Task类型的异步方法

  async Task ReadFileAsync()//async将方法修饰为异步方法
        {
            Console.WriteLine("开始执行异步方法写入文件");
            Thread.Sleep(1000);
            Console.WriteLine("开始执行等待异步方法写入文件");
            await Task.Delay(1000);//程序直接返回到调用异步方法的主程序
           
        }
 private async void button1_Click(object sender, EventArgs e)
        {
            Task task = ReadFileAsync();
            Console.WriteLine("在写入文件期间做了别的事情");
            await task;
            Console.WriteLine("文件写入完毕");

        }

输出

开始执行异步方法写入文件
开始执行等待异步方法写入文件
在写入文件期间做了别的事情
文件写入完毕

注意:以上代码中需要注意的是await关键字把程序的控制点调整到调用异步方法的线程中,下面以ui响应为例子,说明异步编程的作用
private async void button1_Click(object sender, EventArgs e)
{

        Thread.Sleep(5000);//延时5毫秒,虽然方法被标记为异步方法,但是                 如果没有await关键字,程序依然同步执行,当我们点击按钮后,会发现整个ui界面被卡死,如果此时我们想要实现一个更新ui的操作,那就无法实现
        //await Task.Delay(5000);

    }

private async void button1_Click(object sender, EventArgs e)
{

        //Thread.Sleep(5000);
        await Task.Delay(5000);//当我们使用异步关键字之后,点击button之后,发现UI并没有卡顿,这是因为await关键字让程序的控制点返回到了调用异步方法的线程,也就是UI所在的线程。

    }

注意:async除了能够修饰方法之外,还能修饰lamada表达式,如下:

Func<Task> ReadAsync = async () => { await Task.Delay(3000);Console.WriteLine("ddd"); };

ReadAsync 也是一个异步方法和直接定义的被async修饰的方法一样。

2)await关键字

 private async void button1_Click(object sender, EventArgs e)
        {
            await Task.Delay(5000);
        }

下面这个代码是先返回任务,然后再调用await关键字。

 private async void button1_Click(object sender, EventArgs e)
        {
         Task task=    Task.Delay(5000);
         await task;
        }

3)Task.WhenAll
启动所有任务,并且等待所有任务完成,所有的任务并行执行

async Task ReadFile1Async()//async将方法修饰为异步方法
        {
            //Console.WriteLine("开始执行异步方法读取文件1");
            //Thread.Sleep(1000);
            Console.WriteLine("开始执行等待异步方法读取文件1");
            await Task.Delay(3000);//程序直接返回到调用异步方法的主程序
            Console.WriteLine("文件1读取完毕");

        }
        async Task ReadFile2Async()//async将方法修饰为异步方法
        {
            //Console.WriteLine("开始执行异步方法读取文件2");
            //Thread.Sleep(1000);
            Console.WriteLine("开始执行等待异步方法读取文件2");
            await Task.Delay(1000);//程序直接返回到调用异步方法的主程序
            Console.WriteLine("文件2读取完毕");
        }

ReadFile1Async、ReadFile2Async是两个异步读取文件的方法,如果我们想要执行这两个方法,一般我们会按照下面的操作来,这样就会导致程序其实是串行执行的,也就是说读取完了文件1再读取文件2,并不能实现并行的效果。

 private async void button2_Click(object sender, EventArgs e)
        {
            await ReadFile1Async();
            await ReadFile2Async();

            Console.WriteLine("文件1、文件2读取完毕");
        }

输出

开始执行等待异步方法读取文件1
文件1读取完毕
开始执行等待异步方法读取文件2
文件2读取完毕
文件1、文件2读取完毕

如果要实现并行,可以使用Task.WhenAll

 private async void button1_Click(object sender, EventArgs e)
        {

            if (!startRead)
            {
                startRead = true;
                Task[] tasks = new Task[] { ReadFile1Async(), ReadFile2Async() };
                Console.WriteLine("控制点返回");
                await Task.WhenAll(tasks);
                Console.WriteLine("文件1、文件2读取完毕");
                startRead = false;
            }

        }

输出

开始执行等待异步方法读取文件1
开始执行等待异步方法读取文件2
控制点返回
文件2读取完毕
文件1读取完毕

上述代码输出时,因为读取文件2需要的时间短,所以任务先执行完成。
4)Task.WhenAny
可以同时启动多个任务和依次对它们进行处理,并返回首先执行完毕的任务,而不是等待所有的任务执行完毕。

private async void button3_Click(object sender, EventArgs e)
        {
            if (!startRead)
            {
                startRead = true;
                List<Task> tasks = new Task[] { ReadFile1Async(), ReadFile2Async() }.ToList();
                Console.WriteLine("控制点返回");
                Task firstFinishedTask = await Task.WhenAny(tasks);
                Console.WriteLine("有一个文件已经读取完毕");
                startRead = false;
            }
        }

输出

开始执行等待异步方法读取文件1
开始执行等待异步方法读取文件2
控制点返回
文件2读取完毕
有一个文件已经读取完毕
文件1读取完毕

5)完成一个任务后,取消剩余的任务

  bool startRead;//防止重入
  CancellationTokenSource cts = new CancellationTokenSource();
 private async void button1_Click(object sender, EventArgs e)
        {

            if (!startRead)
            {
                startRead = true;
                Task[] tasks = new Task[] { ReadFile1Async(cts.Token), ReadFile2Async(cts.Token) };
                Console.WriteLine("控制点返回");
                await Task.WhenAny(tasks);
                Console.WriteLine("有一个文件读取完毕");
                cts.Cancel();
                Console.WriteLine("取消读取");
                startRead = false;
            }

        }
  private   async Task ReadFile1Async(CancellationToken ct)//async将方法修饰为异步方法
        {
            
            Console.WriteLine("开始执行等待异步方法读取文件1");
            await Task.Run(new Action(() => { for (int i = 0; i < 10; i++) { if (ct.IsCancellationRequested)break; else Console.WriteLine(i); } }), ct);//程序直接返回到调用异步方法的主程序
            Console.WriteLine("文件1读取完毕");

        }
 private    async Task ReadFile2Async(CancellationToken ct)//async将方法修饰为异步方法
        {
          
            Console.WriteLine("开始执行等待异步方法读取文件2");
            await Task.Run(new Action(() => { for (int i = 0; i < 100; i++) { if (ct.IsCancellationRequested)break; else  Console.WriteLine("x" + i); } }), ct);//程序直接返回到调用异步方法的主程序
            Console.WriteLine("文件2读取完毕");
        }

输出

开始执行等待异步方法读取文件1
开始执行等待异步方法读取文件2
控制点返回
0
1
2
3
4
5
6
7
8
9
文件1读取完毕
x0
x1
x2
x3
x4
x5
有一个文件读取完毕
x6
取消读取
文件2读取完毕

6)启动多个任务,并在完成一个任务后,进行处理

 async Task<string> ReadFile3Async(CancellationToken ct)//async将方法修饰为异步方法
        {
            Console.WriteLine("开始执行等待异步方法读取文件1");
            string str = await Task.Run(new Func<string>(() => { for (int i = 0; i < 100; i++) { if (ct.IsCancellationRequested)return "0"; else { Console.WriteLine(i); } } return "99"; }), ct);//程序直接返回到调用异步方法的主程序
            Console.WriteLine("文件1读取完毕");
            return str;
        }
        async Task<string> ReadFile4Async(CancellationToken ct)//async将方法修饰为异步方法
        {
            Console.WriteLine("开始执行等待异步方法读取文件2");
            string str = await Task.Run(new Func<string>(() => { for (int i = 0; i < 1000; i++) { if (ct.IsCancellationRequested)return "x0"; else  Console.WriteLine("x" + i); } return "x999"; }), ct);//程序直接返回到调用异步方法的主程序
            Console.WriteLine("文件2读取完毕");
            return str;
        }

 private async void button3_Click(object sender, EventArgs e)
        {
            if (!startRead)
            {
                startRead = true;
                List<Task<string>> tasks = new Task<string>[] { ReadFile3Async(cts.Token), ReadFile4Async(cts.Token) }.ToList();
                Console.WriteLine("控制点返回");
               
                
                while (tasks.Count > 0)
                {
                    Task<string> firstFinishedTask = await Task.WhenAny(tasks);
                    Console.WriteLine("有一个文件已经读取完毕");
                    tasks.Remove(firstFinishedTask);//等到返回的任务后,就从集合中删掉这个任务。
                    string str = await firstFinishedTask;
                    Console.WriteLine("返回值:"+str);
                }
                startRead = false;
            }
        }

输出

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
开始执行等待异步方法读取文件2
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
x0
控制点返回
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
x1
x2
x3
x4
x5
x6
x7
x8
x9
x10
x11
x12
x13
x14
x15
x16
x17
x18
x19
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
x20
x21
x22
x23
x24
x25
x26
x27
x28
x29
x30
x31
x32
x33
x34
x35
x36
x37
x38
x39
x40
x41
x42
x43
x44
x45
x46
x47
x48
x49
x50
x51
x52
x53
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
x54
x55
x56
x57
x58
x59
x60
x61
x62
x63
x64
x65
x66
x67
x68
x69
x70
x71
x72
x73
x74
x75
x76
x77
x78
x79
x80
x81
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
x82
x83
x84
x85
x86
x87
x88
x89
x90
x91
x92
x93
x94
x95
x96
x97
x98
x99
x100
x101
x102
x103
x104
x105
x106
x107
x108
x109
x110
x111
x112
x113
x114
x115
x116
x117
x118
x119
x120
x121
x122
x123
x124
x125
x126
x127
x128
x129
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
x130
x131
x132
x133
x134
x135
x136
x137
x138
x139
x140
x141
x142
x143
x144
x145
x146
x147
x148
x149
x150
x151
x152
x153
x154
x155
x156
x157
x158
x159
x160
x161
x162
x163
x164
x165
x166
x167
x168
x169
x170
x171
x172
x173
x174
x175
x176
x177
x178
x179
x180
x181
x182
x183
x184
x185
x186
x187
x188
x189
x190
x191
x192
x193
x194
x195
x196
x197
x198
x199
x200
x201
x202
x203
x204
x205
x206
x207
x208
x209
x210
x211
x212
x213
文件1读取完毕
x214
x215
x216
x217
x218
x219
x220
x221
x222
x223
x224
x225
x226
x227
x228
x229
x230
x231
x232
x233
x234
x235
x236
x237
x238
x239
x240
x241
x242
x243
x244
x245
有一个文件已经读取完毕
x246
x247
x248
x249
x250
x251
x252
x253
x254
x255
x256
x257
x258
x259
x260
x261
x262
x263
x264
x265
x266
x267
x268
x269
x270
x271
x272
x273
x274
x275
x276
x277
x278
x279
x280
x281
x282
x283
x284
x285
x286
x287
x288
x289
x290
x291
x292
x293
x294
x295
x296
x297
x298
x299
x300
x301
x302
x303
x304
x305
x306
x307
x308
x309
x310
x311
x312
x313
x314
x315
x316
x317
x318
x319
x320
x321
x322
x323
x324
x325
x326
x327
x328
x329
x330
x331
x332
x333
x334
x335
x336
x337
x338
x339
x340
x341
x342
x343
x344
x345
x346
x347
x348
x349
x350
x351
x352
x353
x354
x355
x356
x357
x358
x359
x360
x361
x362
x363
x364
x365
x366
x367
x368
x369
x370
x371
x372
x373
x374
x375
x376

x377
返回值:999
x378
x379
x380
x381
x382
x383
x384
x385
x386
x387
x388
x389
x390
x391
x392
x393
x394
x395
x396
x397
x398
。
。
。
x9999

7)处理异步应用程序中的重入
1、禁用开关
当执行异步程序时,将按钮的使能属性设置为false,这样按钮就无法触发,或者设置一个变量,使用true/false来阻止按钮再次触发,代码如下:

async Task<string> ReadFile4Async(CancellationToken ct)//async将方法修饰为异步方法
        {
            
            Console.WriteLine("开始执行等待异步方法读取文件2");
            string str = await Task.Run(new Func<string>(() => { for (int i = 0; i < 1000; i++) { if (ct.IsCancellationRequested)return "x0"; else  Console.WriteLine("x" + i); } return "x9999"; }), ct);//程序直接返回到调用异步方法的主程序
            Console.WriteLine("文件2读取完毕");
            return str;
        }


  private async void button2_Click(object sender, EventArgs e)//禁用按钮
        {
            button2.Enabled = false;
            cts = new CancellationTokenSource();
            await ReadFile4Async(cts.Token);
            button2.Enabled = true;
        }
        
 private async void button1_Click(object sender, EventArgs e)//状态标志忽略按钮
        {

            if (!startRead)
            {
                startRead = true;
                cts = new CancellationTokenSource();
                await ReadFile4Async(cts.Token);
                startRead = false;
            }

        }

2、当再次被触发时,取消之前的操作,执行最新的操作

 private async void button3_Click(object sender, EventArgs e)
        {
            if (cts != null)
            {
                cts.Cancel();
            }
            cts = new CancellationTokenSource();
            await ReadFile3Async(cts.Token);
        }

        async Task<string> ReadFile3Async(CancellationToken ct)//async将方法修饰为异步方法
        {
            //Console.WriteLine("开始执行异步方法读取文件1");
            //Thread.Sleep(1000);
            Console.WriteLine("开始执行等待异步方法读取文件1");
            string str = await Task.Run(new Func<string>(() =>
            {
                try
                {
                    for (int i = 0; i < 1000; i++)
                    {
                        Console.WriteLine(i);
                        ct.ThrowIfCancellationRequested();//如果已经取消,那么抛出异常
                    }
                }
                catch (Exception ex)
                {
                }
                return "文件3";
            }), ct);//程序直接返回到调用异步方法的主程序
            Console.WriteLine("文件3读取完毕");
            return str;
        }

3)按照顺序对多个操作进行输出

Task pendWork = null;
    char group;
 private async void button4_Click(object sender, EventArgs e)
        {
            group = (char)(group + 1);
            await Testddd(group);
        }


        private async Task Testddd(char index)
        {
            pendWork = Finished(index);
        }
        private async Task Finished(char index)
        {
            if (pendWork != null)//当下次点击的时候,由于Pendwork不为NULL,所以程序就会阻塞,然后一直到上一次的程序执行完毕,然后接着执行后面的程序,后面的程序都会被阻塞。
                await pendWork;

            await Task.Delay(5000);
            string STR = ((int)index).ToString();
            Console.WriteLine(STR);
        }

8)并行迭代
1、for

 Parallel.For(0, 100, i =>
            {
                Console.WriteLine(i.ToString());
            });

2、foreach以及取消并行循环

 CancellationTokenSource cts = new CancellationTokenSource();
        ParallelOptions po;
  private void TestParall()
        {

            List<int> arrays = new List<int>();

            for (int i = 0; i < 100; i++)
            {
                arrays.Add(i);
            }
            try
            {
                Parallel.ForEach(arrays,po, new Action<int, ParallelLoopState>(
                    (item, parallelLoopState) => { OutputValue(item, parallelLoopState); }));
            }
            catch (OperationCanceledException  ex)
            {
                Console.WriteLine(ex);
               
            }
        }
 private void OutputValue(int i, ParallelLoopState parallelLoopState)
{
    //if (i == 50)
    //    parallelLoopState.Break  ();
    //else
    Console.WriteLine(i);
}

调用

   po = new ParallelOptions() { CancellationToken = cts.Token };
            Task task = Task.Run(TestParall);
            Thread.Sleep(20);
            cts.Cancel();//会取消并行循环,并且会抛出异常被捕获
            task.Wait();

3、中断循环

 CancellationTokenSource cts = new CancellationTokenSource();
        ParallelOptions po;
  private void TestParall()
        {

            List<int> arrays = new List<int>();

            for (int i = 0; i < 100; i++)
            {
                arrays.Add(i);
            }
            try
            {
                Parallel.ForEach(arrays,po, new Action<int, ParallelLoopState>(
                    (item, parallelLoopState) => { OutputValue(item, parallelLoopState); }));
            }
            catch (OperationCanceledException  ex)
            {
                Console.WriteLine(ex);
               
            }
        }
 private void OutputValue(int i, ParallelLoopState parallelLoopState)
        {
            if (i == 50)
                parallelLoopState.Break  ();
            else
            Console.WriteLine(i);

        }

调用

Task task = Task.Run(TestParall);

注意:parallelLoopState用于中断,可以调用break或者stop。

9)并行执行linq查询
1、查询操作符并行查询

List<int> arrayss = new List<int>();
    for (int i = 0; i < 100; i++)
    {
        arrayss.Add(i);
    }
    List<string> ddd = arrayss.AsParallel().Select(new Func<int, string>((i) => i.ToString())).ToList();
 2、取消并行查询
 Task<List<string>> tasks = null;

        tasks = Task.Run<List<string>>(new Func<List<string>>(() =>
        {
            try
            { return arrayss.AsParallel().WithCancellation(cts.Token).Select(new Func<int, string>((i) => i.ToString())).ToList(); }
            catch (OperationCanceledException ex)
            {
                return null;
            }
        }));
        
        cts.Cancel();
        tasks.Wait();
         if (tasks.Result == null)
            Console.WriteLine("true");

3、查询表达式的并行查询

 List<string> ddddd = (from itm in arrayss.AsParallel() select itm.ToString()).ToList();
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

c#上位机

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值