当前位置: 首页 > news >正文

C#多线程

目前C#多线程大部分大部分都是清一色的Task,这里就先主要讲一下Task
本文主要讲解线程的启动,延迟执行,线程等待,线程的异常捕获及线程的取消

1.线程的启动:
主要有三种方式
方式一

        Task task = new Task(() =>
        {
            System.Diagnostics.Debug.WriteLine($"Task开启了一个线程, TheadId:{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
        });

        **task.Start();**

方式二

       Task.Run(()=>         
       {
           System.Diagnostics.Debug.WriteLine($"Run开启了一个线程, TheadId:{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
       });

方式三

TaskFactory factory = new TaskFactory();
factory.StartNew(()=>
{
    System.Diagnostics.Debug.WriteLine($"Factory开启了一个线程, TheadId:{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
});

2. Task延迟执行方法Delay.如下方法

private void TestTask()
{
    Debug.WriteLine("主线程开始");
    Console.WriteLine("Task.Delay Start");
    Stopwatch timer = new();
    timer.Start();

    Task.Delay(100).ContinueWith((s, x) => 
    {
      Debug.WriteLine($"ContinueWith中启动一个线程, TheadId:{Thread.CurrentThread.ManagedThreadId:00}");
    }, null);

    timer.Stop();

    Debug.WriteLine($"Task.Delay耗时:{timer.ElapsedMilliseconds}");
    Console.WriteLine("Task.Delay End");

    Debug.WriteLine("主线程结束");
 }

执行后的代码
主线程开始
Task.Delay耗时:0
主线程结束
ContinueWith中启动一个线程, TheadId:41

由此可见Delay延迟不会阻塞主线程,主线程执行到这里会继续执行.在delay等待一定的时间后再执行ContinueWith中的委托

3.等待一个任务完成就执行另一个动作和等待所有任务完成执行另一个动作

    private void TestTask()
    {
        List<Task> taskList = new List<Task>();
        Task task1 = Task.Run(() => DoWork("设计", 10000));
        Task task2 = Task.Run(() => DoWork("框架", 5000));
        Task task3 = Task.Run(() => DoWork("分析", 3000));
        Task task4 = Task.Run(() => DoWork("对接", 1000));
  
        taskList.Add(task1);
        taskList.Add(task2);
        taskList.Add(task3);
        taskList.Add(task4);

        **int index = Task.WaitAny(taskList.ToArray());**
        Debug.WriteLine($"Task {index + 1} completed first.");
        
		//Task.WaitAll(taskList.ToArray());
		//Debug.WriteLine("all Task completed first.");
    }

    private async Task DoWork(string taskName, int delay)
    {
        Debug.WriteLine($"{taskName} is Start.");
        await Task.Delay(delay); // 模拟工作
        Debug.WriteLine($"{taskName} is completed.");
    }

4.异常的捕获
先看一下下面的代码

    private void ProcessException()
    {
        try
        {
            for (int i = 0; i < 20; i++)
            {
                string k = $"{i}";
                Task.Run(() =>
                {
                    if (k.Equals("8"))
                    {
                        throw new Exception("因为k == 8,这里就异常了");
                    }
                    else if (k.Equals("11"))
                    {
                        throw new Exception("因为k == 11,这里就异常了");
                    }
                    else if (k.Equals("15"))
                    {
                        throw new Exception("因为k == 15,这里就异常了");
                    }
                });
            }
        }
        catch(Exception err)
        {
            Debug.WriteLine(err.Message);
        }
    }

上述代码异常时catch中并没有输出任何东西,这说明多线程内部发生异常时try catch是不能捕获的.
需要对代码进行调整:
1.增加对多任务的等待 wait
2.在catch中增加AggregateException的捕获
代码如下

    private void ProcessException()
    {
        try
        {
            List<Task> list = new();
            for (int i = 0; i < 20; i++)
            {
                string k = $"{i}";
                Task task = Task.Run(() =>
                {
                    if (k.Equals("8"))
                    {
                        throw new Exception("因为k == 8,这里就异常了");
                    }
                    else if (k.Equals("11"))
                    {
                        throw new Exception("因为k == 11,这里就异常了");
                    }
                    else if (k.Equals("15"))
                    {
                        throw new Exception("因为k == 15,这里就异常了");
                    }
                });

                list.Add(task);
            }

            Task.WaitAll(list.ToArray());
        }
        catch(AggregateException err)
        {
            foreach (var inner in err.Flatten().InnerExceptions)
            {
                Debug.WriteLine(inner.Message);
            }
        }
    }

这样改动后,异常最终都会在catch中输出了

5.线程的取消 应用CancellationTokenSource
一个任务长时间运行或多个任务启动执行,如何中间取消了操作,则后续的动作或线程不再继续执行
代码如下
一个任务长时间运行代码如下

private async void CancelThread()
{
    CancellationTokenSource cts = new CancellationTokenSource();

    // 获取 CancellationToken
    CancellationToken token = cts.Token;

    // 启动一个长时间运行的任务,并传递 CancellationToken
    Task longRunningTask = LongRunningOperationAsync(token);

    // 模拟一些主线程上的其他操作
    await Task.Delay(2000); // 等待2秒

    // 请求取消操作
    cts.Cancel();

    try
    {
        // 等待长时间运行的任务完成
        await longRunningTask;
    }
    catch (OperationCanceledException ex)
    {
        Debug.WriteLine($"Operation was canceled: {ex.Message}");
    }
}

private async Task LongRunningOperationAsync(CancellationToken token)
{
    for (int i = 0; i < 10; i++)//5s
    {
        token.ThrowIfCancellationRequested();

        Debug.WriteLine($"Working... {i + 1}");

        await Task.Delay(500, token); // 模拟异步工作,并传递 CancellationToken 以支持取消
    }
    Debug.WriteLine("Operation completed.");
}

多个任务中间任务取消代码如下

    private async void CancelThread()
    {
        CancellationTokenSource cts = new CancellationTokenSource();
        List<Task> tasks = [];

        for (int i = 0; i < 10; i++)
        {
            if (cts.Token.IsCancellationRequested)// 检查是否已经请求了取消
            {
                Debug.WriteLine($"Cancellation requested at task {i}.");
                break;
            }

            int taskId = i;
            tasks.Add(Task.Run(async () => await ExecuteTaskAsync(taskId, cts.Token), cts.Token));

            if (i == 2) 
            {
                cts.Cancel();
                Debug.WriteLine($"{i} Cancellation requested.");
            }
        }

        try
        {
            await Task.WhenAll(tasks);
        }
        catch (OperationCanceledException)
        {
            Debug.WriteLine("One or more tasks were canceled.");
        }

        Debug.WriteLine("All done.");
    }

    private async Task ExecuteTaskAsync(int taskId, CancellationToken token)
    {
        for (int i = 0; i < 3; i++) // 模拟任务的工作
        {
            token.ThrowIfCancellationRequested();
            Debug.WriteLine($"Task {taskId} is working... {i + 1}");
            await Task.Delay(500, token); // 模拟异步工作
        }

        Debug.WriteLine($"Task {taskId} completed.");
    }

6.几种现象

private void TestThread()
{
    for (int i = 0; i < 20; i++)
    {
        Task.Run(() =>
        {
            Thread.Sleep(1000);
            Debug.WriteLine($"{i} 开始执行了");
        });
    }
}

这个方法的输出一直是"20 开始执行了";
修改一下后代码如下

    private void TestThread()
    {
        for (int i = 0; i < 20; i++)
        {
            int k = i;
            Task.Run(() =>
            {
                Thread.Sleep(1000);
                Debug.WriteLine($"{k} 开始执行了");
            });
        }
    }

这时,k就是序号了.

相关文章:

  • UEFI Spec 学习笔记---11 - Protocols — UEFI Driver Model(1)
  • 嵌入式音视频开发(二)ffmpeg音视频同步
  • 大一计算机的自学总结:前缀树(字典树、Trie树)
  • 【Ai】辅助编程利器:Cline、Continue
  • 大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(1)
  • #渗透测试#批量漏洞挖掘#Apache Log4j反序列化命令执行漏洞
  • 基于AdaIN的实时图像风格迁移-照片艺术化
  • 初识LLMs
  • 第四十三篇——零和博弈(鞍点理论):如何找到双方的平衡点?
  • 【Linux系统】生产者消费者模型:基于阻塞队列 BlockingQueue
  • 【笔记】LLM|Ubuntu22服务器极简本地部署DeepSeek+API使用方式
  • 使用apt-rdepends制作软件离线deb安装包
  • 网站搭建基本流程
  • RK3568平台开发系列讲解(PWM篇)SG90 舵机驱动实验
  • 蓝桥杯题目(36进制)
  • Recall(召回率)和 Precision(精确率) 的区别和F1分数
  • UML顺序图的建模方法及应用示例
  • 一、《重学设计模式》-设计模式简介
  • 3.9 用户反馈智能分析实战:从情感识别到产品优化的闭环设计指南
  • 低代码(Low Code)全解析:从概念到应用,从选择到价值
  • 中国医药科技出版社回应发布“男性患子宫肌瘤”论文:正在核查
  • 市场监管总局通报民用“三表”专项检查结果
  • “穿越看洪武”,明太祖及其皇后像台北故宫博物院南院展出
  • 胡塞武装称以色列所有机场均为其打击目标
  • 魔都眼|上海环球马术冠军赛收官,英国骑手夺冠
  • 美国警方:爱达荷州交通事故致7人死亡,8名伤者预计无生命危险