您的位置:首页 > 科技 > 能源 > C#中的线程

C#中的线程

2024/12/23 8:23:44 来源:https://blog.csdn.net/qq_48148522/article/details/141498736  浏览:    关键词:C#中的线程

进程/线程

  • 进程: 记录一个应用程序在运行的过程中,消耗的计算机资源的总记录; 计算机的资源:CPU/磁盘IO/网络
  • 线程:在程序运行的过程中,对于任何一条指令执行的一个最小执行流;
  • 进程和线程:一个进程至少要包含一个线程;
  • 多线程:程序在运行的过程中,可以多个线程同时去执行;
  • 重点: 进程,还是线程----都属于是计算机的资源;

启动线程的多种方式

直接New

Task task = new Task(() =>
{Console.WriteLine($"Start Thread -- {Thread.CurrentThread.ManagedThreadId.ToString("000")}");
});

Task.Run

Task.Run(() =>
{Console.WriteLine($"Start Thread -- {Thread.CurrentThread.ManagedThreadId.ToString("000")}");
});

TaskFactory

TaskFactory taskFactory = new TaskFactory();
taskFactory.StartNew(() =>
{Console.WriteLine($"Start Thread -- {Thread.CurrentThread.ManagedThreadId.ToString("000")}");
});

Task.Factory.StartNew

Task.Factory.StartNew(() =>
{Console.WriteLine($"Start Thread -- {Thread.CurrentThread.ManagedThreadId.ToString("000")}");
});

线程等待

Thread.Sleep 和 Task.Delay的区别

  1. Thread.Sleep:

等待主线程 直接卡顿界面 影响用户的体验

Thread.Sleep(1000);
  1. Task.Delay:

不会等待主线程 一般伴随 ContinueWith 一起使用

表示等待多长时间之后 可以执行一个回调方法

Task.Delay(1000);
=================
Task.Delay(1000).ContinueWith(task =>
{});

Task.WaitAll

使用场景:

如果希望所有的线程都执行结束后,再执行一段业务逻辑

特点:

等待(等待主线程/UI线程)所有线程执行完 才执行以下业务 会卡顿界面

Task.WaitAll(tasks.ToArray()); 

Task.WaitAny

使用场景:

如果希望这一堆任务中,其中只要有一个任务执行完毕就继续往后执行

特点:

等待(等待主线程/UI线程)任一一个线程执行完 才执行以下业务 会卡顿界面

Task.WaitAny(tasks.ToArray());

Task.Factory.ContinueWhenAll

使用场景:

既等待也不卡界面的等待方式 等待一堆线程执行完之后 继续往下执行

特点:

等待一堆线程执行完之后 继续往下执行

第二个参数是执行完的Task集合

Task.Factory.ContinueWhenAll(tasks.ToArray(), taskList =>
{Console.WriteLine("不卡顿界面所有线程执行之后继续往后执行!!!");
});

Task.Factory.ContinueWhenAny

使用场景:

既等待也不卡界面的等待方式 等待一堆线程中,任何一个线程执行完之后 继续往下执行

特点:

等待一堆线程中,任何一个线程执行完之后 继续往下执行

第二个参数是执行完的Task

Task.AsyncState:执行完的状态对象,可以在创建的时候传值

Task.Factory.ContinueWhenAny(tasks.ToArray(), task =>
{Console.WriteLine($"{task.AsyncState}执行结束:不卡顿界面任何一个线程执行完之后继续往后执行!!!");
});

Task.AsyncState传值Demo:

必须使用:Task.Factory.StartNew,创建一个带有Object的Action对象

List<Task> tasks = new List<Task>();
for (int i = 0; i < 10; i++)
{int k = i;tasks.Add(Task.Factory.StartNew(o =>{Console.WriteLine($"开始逻辑:{k}");Thread.Sleep(200);Console.WriteLine($"结束逻辑:{k}");}, $"任务{k}"));
}

执行如下:

多线程异常处理

  • 在多线程的内部发生的异常 外部的 try-catch 是捕捉不到的
  • 如果把 try-catch 写在task内部可以捕捉到,不过是单个线程的捕捉
  • 捕捉异常:线程等待 Task.WaitAll
  • 捕捉到的多线程异常,有什么特点:AggregateException:类型的异常
  • 一个 try 可以对应多个 catch,异常类型的匹配优先具体类型,如果没有再匹配抽象

Demo代码:

try
{List<Task> taskList = new List<Task>();for (int i = 0; i < 20; i++){int k = i;taskList.Add(Task.Run(() =>{if (k == 6)throw new Exception("k == 6");if (k == 9)throw new Exception("k == 9");if (k == 11)throw new Exception("k == 11");}));}Task.WaitAll(taskList.ToArray());
}
// 如果多个catch 优先匹配具体类型 如果没有才会去匹配抽象
catch (AggregateException ex)
{foreach (var item in ex.InnerExceptions){Console.WriteLine(item.Message);}
}
catch (Exception ex)
{Console.WriteLine(ex.ToString());
}

线程取消

  • 线程不能从外部取消,线程只能自己取消自己
  • 来一个信号量:每个线程去判断这个信号量,如果有线程发生异常了,就去修改这个信号量
  • 线程取消的标准方案:CancellationTokenSource,提供IsCancellationRequested属性,默认值为false
  • 同时提供Cancel方法, IsCancellationRequested: false --> true

Demo代码:

CancellationTokenSource cts = new CancellationTokenSource();
List<Task> taskList = new List<Task>();
for (int i = 0; i < 200; i++)
{int k = i;taskList.Add(Task.Run(() =>{// 线程取消 只能取消还没有判断的线程if (cts.IsCancellationRequested){Console.WriteLine($"开始:{Thread.CurrentThread.ManagedThreadId.ToString("000")}");}else{Console.WriteLine($"Cancel结束:{Thread.CurrentThread.ManagedThreadId.ToString("000")}");cts.Cancel();}if (k == 6)cts.Cancel();},cts.Token));
}

临时变量

  • 多线程:延时启动不会阻塞主线程
  • 因为task启动的时候需要请求计算机需要内存然后去启动,这个时候for循环如果很少的话,就会执行完毕,导致i为最大的++
  • 如果需要一个中间变量去接受i,然后输出,必须在循环里面:因为每次循环都是新的变量,如果把变量定义在for循坏外面则和输出i一样。
for (int i = 0; i < 20; i++)
{int k = i;Task.Run(() =>{Console.WriteLine(k);});
}

线程安全

多线程和单线程执行逻辑一致就是线程安全

  1. 加锁:标准锁:锁静态对象
    1. private static readonly object obj_lock = new object();
  1. 使用线程安全集合:
    1. ConcurrentDictionary<>
  1. 分片执行:
    1. 出现线程安全的主要场景,多个线程同时去操作变量、同时操作计算机文件
    2. 避开让多个线程有机会同时去操作同一块数据

加锁案例:

private static readonly object obj_lock = new object();List<Task> taskList = new List<Task>();int AsyncNum = 0;for (int i = 0; i < 1000_0; i++){taskList.Add(Task.Run(() =>{lock (obj_lock){AsyncNum++;}}));}Task.WaitAll(taskList.ToArray());Console.WriteLine(AsyncNum); 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com