C# 多线程

C# 多线程理解

Task中await和.Result和GetAwaiter().GetResult()方法的区别

await不会阻塞调用它的线程,而GetAwaiter().GetResult()和.Result则会阻塞调用它的线程,但都会阻塞当前线程

Task.Run 和 Task.Factory.StartNew 区别

Task.Run 是在 dotnet framework 4.5 之后才可以使用,但是 Task.Factory.StartNew 可以使用比 Task.Run 更多的参数,可以做到更多的定制。

可以认为 Task.Run 是简化的 Task.Factory.StartNew 的使用,除了需要指定一个线程是长时间占用的,否则就使用 Task.Run

Task.Run 用的是线程池,也就是不是调用这个函数就会一定创建一个新的线程,但是会在另一个线程运行。

Task.Run和Task.Factory.StartNew都会创建一个新的线程

两个函数最大的不同在于 Task.Factory.StartNew 可以设置线程是长时间运行,这时线程池就不会等待这个线程回收

Task.Factory.StartNew(() =>
{
    for (int i = 0; i < 100; i++)
    {
        var foo = 2;
    }
    Console.WriteLine("进行 线程" + Thread.CurrentThread.ManagedThreadId);
}, TaskCreationOptions.LongRunning);

所以在需要设置线程是长时间运行的才需要使用 Task.Factory.StartNew 不然就使用 Task.Run

调用 Task.Run(foo) 就和使用下面代码一样

Task.Factory.StartNew(foo, 
    CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

await是阻塞当前方法继续调用,但不阻塞当前线程(返回Task/Task)
使用 .Result 或者 .Wait() ,这样会在阻塞当前线程同时仍然会启动另外一个后台线程去执行这个函数,得不偿失,大部分情况下我们只需要直接使用 await 关键字即可。
在非常特殊的情况下,如果非得阻塞当前线程去执行这个 Task,可以考虑使用 .GetAwaiter().GetResult()。两者效果基本一样,阻塞当前线程同时会启动另外一个后台线程。但是如果使用 .Result 或 .Wait() 发生错误时会抛出 AggregateExecption ,而使用 .GetAwaiter().GetResult(),则是会抛出实际的 Excepiton ,Excepiton 中的堆栈也符合我们所期望的。

主要是区分两个概念:
1.阻塞当前方法调用;
2.阻塞当前线程;

Demo源码

/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
    Console.WriteLine("我是主线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
    //task用法一  
    Task task1 = new Task(() => MyAction());
    task1.Start();

    //task用法二  
    var strRes = Task.Run<string>(() => { return GetReturnStr(); });
    Console.WriteLine(strRes.Result);

    Console.WriteLine("---------------------------------");
    Console.WriteLine("①我是主线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
    var testResult = TestAsync();
    testResult.GetAwaiter().OnCompleted(() => {
        var id = testResult.Id;
        Console.WriteLine("My name is: " + id);
    });

    //var te = testResult.Result;
    Test();
    Console.WriteLine("主线程执行完毕");
    Console.ReadKey();
}
static async Task TestAsync()
{
    Console.WriteLine("②调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId,
        DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss.fff"));
    var name = GetReturnResult();
    Console.WriteLine("④调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId,
        DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss.fff"));
    Console.WriteLine("⑥得到GetReturnResult()方法的结果一:{0}。当前时间:{1},线程ID:{2}", await name,
        DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss.fff"), Thread.CurrentThread.ManagedThreadId);
    Console.WriteLine("⑥得到GetReturnResult()方法的结果二:{0}。当前时间:{1},线程ID:{2}", name.GetAwaiter().GetResult(),
        DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss.fff"), Thread.CurrentThread.ManagedThreadId);
}

static async Task<string> GetReturnResult()
{
    Console.WriteLine("③执行Task.Run之前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId);

    return await Task.Run(() =>
    {
        Thread.Sleep(1000);
        Console.WriteLine("⑤GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId);
        return "我是返回值";
    });
}

static void MyAction()
{
    Console.WriteLine("我是新进程,线程ID:() MyAction " + Thread.CurrentThread.ManagedThreadId);
}

static string GetReturnStr()
{
    return "我是返回值 ,GetReturnStr 线程ID:" + Thread.CurrentThread.ManagedThreadId;
}
static async void Test()
{
    Thread.Sleep(2000);
    Console.WriteLine("Test 线程ID:" + Thread.CurrentThread.ManagedThreadId);
    await Task.Run(() => {
        Thread.Sleep(5000);
        Console.WriteLine("Test方法等待5秒开始执行");
    });
    Console.WriteLine("Test 线程ID:" + Thread.CurrentThread.ManagedThreadId);
}

posted @ 2022-07-15 17:04  代号-9527  阅读(176)  评论(0编辑  收藏  举报