异步编程,任务与并行编程,任务同步

异步编程:

同步:按照顺序,一件事情做完了才能接着做一件事情,比如主函数中执行多个顺序方法和语句,只有上一条语句或者方法执行完毕才能执行下一个方法或者语句。
异步:开始一个任务(比如一个方法)后,让任务在另一个线程中执行,本线程可以继续执行别的事情,然后等待那个任务执行完毕

比如一个UI按钮,UI就相当于一个主函数,而按钮事件就是调用的方法,如果使用同步,那么在方法执行期间,整个ui界面就会发生阻塞,从而导致无法做任何操作

多线程和异步不是同一个概念,多线程是用于实现异步的一种方式。
异步可以用线程来实现也可以不用线程实现,C#使用线程来实现

 

传统方法一,异步模式,使用委托的BeginInvoke及EndInvoke

class Program
    {
        public delegate int FooDelegate(string s);

        static void Main(string[] args)
        {
            Console.WriteLine("主线程" + Thread.CurrentThread.ManagedThreadId);

            FooDelegate fooDelegate = Foo;
            IAsyncResult result = fooDelegate.BeginInvoke("Hello World", null, null);

            Console.WriteLine("主线程继续执行做其他事...");

            int n = fooDelegate.EndInvoke(result);
            Console.WriteLine("回到主线程" + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("结果是" + n);
            Console.ReadKey(true);

        }

        public static int Foo(string s)
        {
            Console.WriteLine("函数所在线程" + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("异步线程开始执行:" + s);
            Thread.Sleep(1000);
            return s.Length;
        }
    }

 

传统方法二,基于事件,对于完成后打印结果等后续语句,使用委托回调

class Program_Callback
    {
        public delegate int FooDelegate(string s);

        static void Main(string[] args)
        {
            Console.WriteLine("主线程" + Thread.CurrentThread.ManagedThreadId);

            FooDelegate fooDelegate = Print;
            IAsyncResult result = fooDelegate.BeginInvoke("Hello World", PrintComplete, fooDelegate);//这里回调PrintComplete方法
            Console.WriteLine("主线程继续执行做其他事...");
            Console.ReadKey(true);

        }

        public static int Print(string s)
        {
            Console.WriteLine("函数所在线程" + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("异步线程开始执行:" + s);
            Thread.Sleep(1000);
            return s.Length;
        }

        public static void PrintComplete(IAsyncResult result)
        {
            (result.AsyncState as FooDelegate).EndInvoke(result);
            Console.WriteLine("当前线程结束" + result.AsyncState.ToString());
        }
    }

 

最新方式,基于任务,使用await和async关键字和Task类型

await 表示等待任务执行,用来调用返回任务的方法,或者阻塞等待一个任务执行完毕
async修饰一个方法,表示其中有await语句

await task相当于EndInvoke()用于阻塞等待任务线程执行完毕

    class Program_awaitAndAsync
    {
        static async Task Main(string[] args)
        {
            Task<double> task = FacAsync(10);

            Console.WriteLine("主函数继续执行。。。");
            for (int i = 0; i < 10; i++) Console.WriteLine("main");

            double result = await task;//此处await对程序进行阻塞,直到task执行完毕 才会执行接下来的语句

            Console.WriteLine("结果为"+result);
            Console.ReadKey(true);
        }

        public static Task<double> FacAsync(int n)
        {
            //Run会开启一个新的线程来执行委托的程序
            return Task<double>.Run(() => {
                double s = 1; for (int i = 1; i < n; i++) {
                    s = s + i;
                    Console.WriteLine("task");
                }
                return s;
            });
        }

        async void Test()
        {
            double result = await FacAsync(10);
            Console.WriteLine(result);
        }
    }

 

任务与并行编程:

并行任务库TPL(Task Parallel Library)
最重要的是Task类和Parallel类。
Task类,是利用线程池来进行任务的执行,比直接用ThreadPool更优化,更方便
Parallel类,是 并行执行任务类的实用类,好处是可以隐式地包装使用Task,更方便

多任务与多线程的关系,可以粗略的认为多任务的目标是充分使用多核cpu,而多线程却不一定,可能多线程使用的是同一个cpu,并且多任务的底层实现上使用到了多线程。

使用Task.Run方法来得到Task实例

Task<double> task=Task.Run(()=>SomeFun());
double result=task.Result;//等待直到获得结果
可以使用Task.WaitAll(task数组)等待所有task执行完毕
可以使用task.ContinueWith(另一个task)来使得task一个一个执行

    class Program
    {
        static void Main(string[] args)
        {
            Task<double>[] tasks = {
                Task.Run(()=>SomeFun()),
                Task.Run(() => SomeFun())
            };
            Thread.Sleep(1);
            for(int i = 0; i < tasks.Length; i++)
            {
                Console.WriteLine(tasks[i].Status);//状态
                Console.WriteLine(tasks[i].Result);//获取结果,这里相当于wait,会等到任务结束
            }

            //Task.WaitAll(tasks);//等待所有任务结束
        }

       static double SomeFun()
        {
            return 1;
        }
    }

Parallel类的使用:

Parallel.Invoke(Action[] actions);并行执行多个任务,直到完成
Parallel.For(0,100,i=>{...})
Parallel.Foreach(list,Item=>{...})
示例:并行计算矩阵乘法

class Program_Parallel
    {

        //一般的矩阵相乘
        static void MultiMatrixNormal(double[,] matA, double[,] matB, double[,] result)
        {
            int m = matA.GetLength(0);
            int n = matA.GetLength(1);
            int t = matB.GetLength(1);

            for(int i = 0; i < m; i++)
            {
                for(int j = 0; j < t; j++)
                {
                    double temp = 0;
                    for(int k = 0; k < n; k++)
                    {
                        temp += matA[i, k] * matB[k, j];
                    }
                    result[i, j] = temp;
                }
            }
        }

        //使用了多任务的矩阵相乘
        static void MultiMatrixParallel(double[,] matA, double[,] matB, double[,] result)
        {
            int m = matA.GetLength(0);
            int n = matA.GetLength(1);
            int t = matB.GetLength(1);

            Parallel.For(0, m, i =>
              {
                  {
                      for (int j = 0; j < t; j++)
                      {
                          double temp = 0;
                          for (int k = 0; k < n; k++)
                          {
                              temp += matA[i, k] * matB[k, j];
                          }
                          result[i, j] = temp;
                      }
                  }
             });
            
        }
    }

并行Linq(即PLinq)

只要在集合上加上个.AsParallel()
var a=(from n in persons.AsParallel() where n.Age>20&&n.Age<25 select n).ToList();

 

下面是摘抄自https://blog.csdn.net/birdfly2015/article/details/105024216

一、区别与联系
从技术发展的时间线上,微软推出这几个的顺序是:Thread=>ThreadPool=>Task=>Parallel

Thread
优点在于提供了丰富的多线程操作API,缺点在于线程个数的使用不加限制,以及无法复用线程,因此推出了TheadPool技术
ThreadPool
优点在于解决了线程个数的限制以及线程的复用,缺点在于API较少而且线程等待问题解决起来较为麻烦,因此推出了Task技术
Task
优点在于丰富的API,以及多线程的方便管理,缺点在于线程数量控制较为麻烦,因此推出了Parrel技术
Parallel
优点在于丰富的API,多线程方便管理,线程数量控制简单,但是主线程也会参与计算,导致主线程的阻塞问题(但是这个问题可以通过包一层来解决)
二、最佳实践
1.通常绝大部分问题通过Task就能很好的解决
2.最好不要使用线程嵌套线程
3.注意访问公共资源需要加锁
4.多线程参数的传递
5.多线程完成时的判断

posted @ 2020-05-03 12:23  小辉歌  阅读(339)  评论(0编辑  收藏  举报