多线程与异步

44 阅读1分钟

多线程

多线程 using System.Threading

//创建了一个与主线程并行的子线程,lambda表达式中代码属于子线程
Thread thread = new Thread(() =>
                {
                    _viewModel.SaveCustomer(name, idNumber, address);
                });
                //启动
                thread.Start();
//让线程进入静默状态,单位ms
thread.Sleep(1000);

前台线程、后台线程与线程池托管

持续运行的子线程被称作前台线程,只有前台线程结束后程序才能被关闭 后台线程会随着主程序的结束而结束 .net中,除主线程外,任何线程都可以在任何时候切换为前台或后台线程

Thread thread = new Thread(() =>
                {
                });
                //切换为后台线程
                thread.IsBackgroud = true;
                //启动
                thread.Start();

托管在线程池中的线程全部是后台线程,通过new thread创建的线程全部是前台线程 线程池:通过线程池创建的线程生命周期托管在线程池内,线程池可以在不新建线程的情况下重复使用已经完成的线程,使线程数量维持在较低的水平

//通过线程池创建线程
//使用线程池创建线程需要参数o(代表object)
ThreadPool.QueueUserWorkItem((o) => {
                Console.WriteLine($"线程id {Thread.CurrentThread.ManagedThreadId}");
            });

结束线程与CancellationToken

不管程序有多少进程,进程间的资源相互共享,故C#封装了一个取消令牌CancellationToken

CancellationTokenSource tokenSource = new CancellationTokenSource();
//tokenSource是一个变量,需要将其传入子线程中
 Thread thread = new Thread(() => { PrintHello(tokenSource.Token); });
            thread.Start();

            // 下载文件
            Thread.Sleep(5000);

            // 关闭子线程
            //tokenSource.Cancel();
            // 3s后关闭
            tokenSource.CancelAfter(3000);

            Console.WriteLine("退出主程序");
        }

        private static void PrintHello(CancellationToken token)
        {
        //IsCancellationRequested 取消token请求是否被提交
            while (!token.IsCancellationRequested)
            {
                Thread.Sleep(1000);
                Console.WriteLine("Hello from PrintHello");
            }
        }

不将前台线程切换为后台线程的情况下,实现主线程关闭并且子线程也关闭

Join 与 IsAlive

thread.Join();在主线程调用join方法后,主线程与子线程绑定,此时主线程被挂起,等待子线程执行结束后继续执行。

或者检测thread.IsAlive属性,判断子线程是否在工作

while(thread.IsAlive)
{
    Console.WriteLine("子线程工作中");
    Thread.Sleep(100);
}

资源竞争与线程锁lock

lock关键字,用于解决资源竞争的问题

static object lockedObj = new object();
static void AddText()
{
    lock (lockedObj)
    {
        //                                        取得当前进程的id
        File.AppendAllText(@"D:\test.txt", $"开始 {Thread.CurrentThread.ManagedThreadId} ");
        Thread.Sleep(100);
        File.AppendAllText(@"D:\test.txt", $"结束 {Thread.CurrentThread.ManagedThreadId} ");
    }
}

异步 Task

同步,程序调用某方法,需要等待执行完成后才进行下一步操作,异步,程序调用某方法不做等待操作,在处理完成前就返回该方法继续执行接下来的操作

使用Task类创建异步处理并行逻辑(使用多线程处理并发)异步比多线程性能高

//当123没有依赖关系时
Task.Run(()=>{
calculate1();
})
Task.Run(()=>{
calculate2();
})
Task.Run(()=>{
calculate3();
})

//如果3需要12的执行结果
var task1 = Task.Run(()=>{
return calculate1();
})
var task2 = Task.Run(()=>{
return calculate2();
})
//等待12执行结束
Task.waitAll(task1,task2)
//获得异步等待对象
var awaiter1 = task1.GetAwaiter();
//获得执行结果
var result1 = awaiter1.GetResult();
var awaiter2 = task1.GetAwaiter();
var result2 = awaiter1.GetResult();
calculate3(result1,result2);

//在此基础上2又依赖1的执行结果
var task1 = Task.Run(() =>
            {
                return Calculate1();
            });
            //等待1执行完成
            var awaiter1 = task1.GetAwaiter();
            //当1执行完成后OnCompleted就会被调用
            awaiter1.OnCompleted(() =>
            {
                var result1 = awaiter1.GetResult();
                //在异步逻辑中继续创建异步逻辑
                var task2 = Task.Run(() =>
                {
                    return Calculate2(result1);
                });

                var awaiter2 = task2.GetAwaiter();
                awaiter2.OnCompleted(() =>
                {
                    var result2 = awaiter2.GetResult();
                    var result = Calculate3(result1, result2);
                    Console.WriteLine(result);
                });
            });

异步方法 async和await

需要使用async关键字修饰 返回类型void Task Task<T> IAsyncEnumerable<T>(用于处理数据流) Task Task<T>类型会返回调用方然后开启新的进程继续其他工作,同时当前进程挂起,等待没有完成的工作 其中需要至少包含一个await表达式表示异步执行的任务 命名规范:异步方法要以Async结尾 async函数只能被async函数调用

static async void Calculate()
{
    // Task -> 异步
    // Thread -> 线程
    //result1的数据类型由Calculate1Async的泛型决定
    var result1 = await Calculate1Async();
    var result2 = await Calculate2Async(result1);
    var result = await Calculate3Async(result1, result2);
    Console.WriteLine(result);
}

static async Task<int> Calculate1Async()
{
    var result = 3;
    Console.WriteLine($"Calculate1: {result}");
    //task中的等待
    await Task.Delay(3000);
    return result;
}

static async Task<int> Calculate2Async(int a)
{
    var result = a*2;
    Console.WriteLine($"Calculate2: {result}");
    await Task.Delay(2000);
    return result;
}

static async Task<int> Calculate3Async(int a, int b)
{
    var result = a + b;
    Console.WriteLine($"Calculate3: {result}");
    await Task.Delay(1000);
    return result;
}

异步,多线程,并发,并行

使用Task.Factory.StartNew()创建新的异步 异步不会在后台创建新的线程,异步是回调而不是多线程执行 异步在执行结束后会创建一个线程通知主程序,这个过程就叫做回调

并发 两只地鼠独立完成一件事情,装卸书的过程中不会排队 并行 使用多个节点 服务器集群部署相同的服务