C# --- 多线程和Task-based Asynchronous Pattern

多线程和异步编程的区别

  • 异步编程是一种编程思想, 意思是在遇到一些耗时的任务的时候,不需要等待,而是可以做其他的事情,
  • 比如在代码中遇到耗时任务时 (比如和数据库交互, 发送网络请求, 耗时的计算任务), 我们通常希望代码可以多任务同时进行, 保证不阻塞主线程 (比如我们不希望当进行网络请求时, 软件界面卡死)
  • 多线程是计算机中的一个概念,线程在进程之中,多个线程可以同时运行,处理不同的事务
  • 通常异步编程是通过多线程来实现的.

Thread 和 Task 的区别

Thread

  • Thread是low-level concept, 代表计算机中某个进程中的一个线程.
  • 多个进程可以同时完成不同的任务
//Example:C#中使用Thread类完成多线程编程
using System;
using System.Threading;
 
class Program
{
    static void Main()
    {
        // create a new thread
        Thread t = new Thread(Worker);

        // start the thread
        t.Start();
 
        // do some other work in the main thread
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("Main thread doing some work");
            Thread.Sleep(100);
        }
 
        // wait for the worker thread to complete
        t.Join();
 
        Console.WriteLine("Done");
    }
 
    static void Worker()
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("Worker thread doing some work");
            Thread.Sleep(100);
        }
    }
}
  • 在Java中以及其他一些语言中,多线程编程通常是通过以上的例子来实现的,程序员需要手动创建线程或者线程池来完成(Java多线程)
  • 但是多线程编程难度比较高,不适当的线程创建和管理都会极大的影响代码性能
  • 所以在C# 提供了一套封装,可以让程序员更方便和安全的使用多线程. 这套封装就是
    TAP — Task-based Asynchronous Pattern 也就是经常见到的 task/async/await 关键字

Task

  • TAP — Task-based Asynchronous Pattern 是对Thread的封装,方便我们更好的使用多线程,进行异步编程.
  • TAP封装了线程池,在TAP中,可以使用关键字会触发使用线程池中的线程 (async/await关键字)
  • 对于I/O bound 任务,如网络请求,文件读写等有一些优化

TAP — Task-based Asynchronous Pattern

  • C#的异步机制简称为TAP, 也就是核心是基于TaskTask<T> objects, 同时通过 asyncawait 关键字完成
  • A Task is a future or a promise. Basically, a Task “promises” to return you a T, but not right now honey, I’m kinda busy, why don’t you come back later?

TAP的两种使用方式之— async/await

  • For I/O-bound code, you await an operation that returns a Task or Task<T> inside of an async method.

await

  • await 表示从这里开始, 代码必须要等待异步方法完成才可以继续进行, 并将控制权交给上一级方法, 并返回一个Promise Task<TResult>

async

  • async 表示这个方法内部调用了 async方法并使用了await
  • 如果一个异步方法没有使用await关键字, 则代码则会同步运行.
public async Task<int> GetUrlContentLengthAsync() {
	var client = new HttpClient();
	
	// 调用 GetStringAsync 进行网络请求. GetStringAsync 是一个耗时任务, 需要等待请求网站反应
	// 此时 GetStringAsync 将控制权交给它的 GetUrlContentLengthAsync, 也就是继续往下运行的意思
	// GetStringAsync 返回 Task<TResult> 并赋值给 getStringTask, TResult在这里是一个string类型. 
	// getStringTask表示这里有个Task,但是还没有完成, 并保证完成后会有一个实际的string值 (一个Promise)
	Task<string> getStringTask = client.GetStringAsync("www.google.com");
	
    // DoIndenpendentWork 是一个不需要上面的返回值的一个方法, 继续正常运行
	DoIndenpendentWork();
	
     //此时不需要要 getStringTask的任务已经做完了,必须要等待 getStringTask 拿到string value
	//如果 getStringTask 还是没有完成, 则 GetUrlContentLengthAsync 会将控制权交给上一级方法.
	//并同样返回一个 Task<TResult>. 这个返回值保证了这里最终会有一个string value, 也就是一个Promise
	//当 GetStringAsync 完成时, 代码在这里回调 (getStringTask这个Task里会存进拿到的string value) 
	//继续运行
	string contents = await getStringTask;
	return contents.length;
}

void DoIndependentWork() {
	Console.WriteLine("Working...");
}

异步方法的返回值

  • Task, 没有返回值的异步方法必须返回Task
  • 有返回值的异步方法返回 Task<TResult> 其中TResult为实际返回类型
  • Event Handler 返回值可以是void
  • IAsyncEnumerable<T>, for an async method that returns an async stream

Example: 返回Task

public static async Task DisplayCurrentInfoAsync()
{
    await WaitAndApologizeAsync();

    Console.WriteLine($"Today is {DateTime.Now:D}");
    Console.WriteLine($"The current time is {DateTime.Now.TimeOfDay:t}");
    Console.WriteLine("The current temperature is 76 degrees.");
}

static async Task WaitAndApologizeAsync()
{
    await Task.Delay(2000);

    Console.WriteLine("Sorry for the delay...\n");
}

Example: 返回 Task<Result>

public static async Task ShowTodaysInfoAsync()
{
    string message =
        $"Today is {DateTime.Today:D}\n" +
        "Today's hours of leisure: " +
        $"{await GetLeisureHoursAsync()}";

    Console.WriteLine(message);
}

static async Task<int> GetLeisureHoursAsync()
{
    DayOfWeek today = await Task.FromResult(DateTime.Now.DayOfWeek);

    int leisureHours =
        today is DayOfWeek.Saturday || today is DayOfWeek.Sunday
        ? 16 : 5;

    return leisureHours;
}
// Example output:
//    Today is Wednesday, May 24, 2017
//    Today's hours of leisure: 5

TAP的两种使用方式之— Task.Run()

  • For CPU-bound code, you await an operation that is started on a background thread with the Task.Run method.
//Example 1
public void Demo()
{
	var taskParameters = new List<Task>();
	foreach (var taskParameter in taskParameters)
	{
	   tasks.Add(RunMyTask(taskParameter));
	}
	Task.WaitAll(tasks.ToArray());
}

private Task RunMyTask(TaskParameter taskParameter)
{
	var task = Task.Run(() => {
		//some CPU-Bound code, such as calculation,may use taskParameter here
	});
	
	return task;
}

//Example 2
//**CPU-bound example: Perform a calculation for a game**
private DamageResult CalculateDamageDone()
{
    // Code omitted:
    //
    // Does an expensive calculation and returns
    // the result of that calculation.
}

calculateButton.Clicked += async (o, e) =>
{
    // This line will yield control to the UI while CalculateDamageDone()
    // performs its work. The UI thread is free to perform other work.
    var damageResult = await Task.Run(() => CalculateDamageDone());
    DisplayDamage(damageResult);
};

为什么Java没有类似TAP的机制

  • Java团队认为TAP机制不能有效的将异步和同步代码相结合 比如all methods in C# doing an await have to be marked as async. 而且异步方法和同步方法在编译阶段是完全不同的, 具体见Oracle工程师的讲座 https://www.youtube.com/watch?v=r6P0_FDr53Q&t=1231s
  • 所以Java团队推出了轻量级线程 aka fibers/lightweight threads 降低线程的使用代价
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值