CSharpe线程

什么是进程?

一个应用程序的运行---对标于一个进程----虚拟词;

所谓的进程---记录了程序运行所消耗的各种各样的资源;

什么是线程?

就是计算机程序在运行的时候,执行指令的最小的执行流~ 程序

的运行---很多的并发操作,任何一个指令的执行都是需要通过线程来完成;

一个进程至少要包含一个线程;进程退出,线程也是自动消失;

什么是多线程?

随着技术的发展---业务的需求---需要指令的并发执行;

同时执行多种指令(线程来执行的);

和CPU的核数有关~~

C#如何操作线程

  1. Thread(很少用)
  2. ThreadPool(线程池)
  3. Task(主流-----重点)

Thread

Thread:来自于System.Threading的一个密封类,它是在.net Framwork1.0时代出现的,在C#中用来操作计算机资源线程的一个帮助类库;

1. Thread如何开启一个线程呢?

多线程因为是无序的,调试不太好调试,只能通过写日志,输出结果,根据结果来判断thread的特点.

private void btn_Thread_Click(object sender, EventArgs e)
{
Debug.WriteLine($"***************Main Thread start: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** "); Thread thread = new Thread(() =>
{
Debug.WriteLine($"***************Thread start: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** "); Debug.WriteLine($"***************Thread end: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
}); thread.Start(); Debug.WriteLine($"***************Main Thread end: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
}

结果

2. Thread中常见的API

thread.Suspend(); // 线程暂停
thread.Resume(); // 线程恢复
thread.Abort(); // 线程终止 1.线程等待的:ThreadState有多种状态;如果线程停止了,状态会修改; while (thread.ThreadState != System.Threading.ThreadState.Stopped) //如果线程没有停止;
{
Thread.Sleep(500); //当前休息500ms 不消耗计算机资源的
} 2.自己支持的线程等待:
thread.Join();//等待线程中的内容执行完毕;继续往后;
thread.Join(500);//等待500ms,过时不候;
thread.Join(TimeSpan.FromMilliseconds(500));//等待500ms,过时不候; thread.IsBackground = true;// 是后台线程:程序强制关掉,线程也就随之消失了;
thread.IsBackground = false; //是前台线程:程序强制关掉,线程会等待,内部的行为执行完毕,然后才结束;
thread.Start();

3. thread的扩展封装

多线程;异步执行;

不阻塞界面;

无序性---多个动作。如果使用多线程,是无法控制顺序的。

现在有两个动作 使用了2个委托 必须是多线程执行的 要求两个委托按顺序执行。

 Action action = () => { Debug.WriteLine("this is first run"); };

 Action action2 = () => { Debug.WriteLine("this is second run"); };

 private void button1_Click(object sender, EventArgs e)
{
callBack(action, action2);
} private void callBack(Action action,Action action1)
{
Thread t= new Thread(() =>
{
action();
action1();
});
t.Start();
}

如果有一个带返回值的委托,需要你要多线程执行;

 Func<int> func = () => { return DateTime.Now.Year; };

 private void button1_Click(object sender, EventArgs e)
{
Func<int> func1= CallBack<int>(func);
Debug.WriteLine("t****************");
Debug.WriteLine("t****************");
Debug.WriteLine("t****************");
Debug.WriteLine("t****************");
Debug.WriteLine("t****************");
Debug.WriteLine("t****************");
int iResult=func1();
Debug.WriteLine(iResult); }
private Func<T> CallBack<T>(Func<T> func)
{
T result = default(T);
Thread t = new Thread(() =>
{
result = func();
});
t.Start(); return new Func<T>(() => {
t.Join();
return result; }); }

threadpool

Thread对比Threadpool:Api很多,功能繁多;使用起来,不好控制;让开发者试用起来并不友好;

Thread对线程的数量管控,全部都需要让程序员自己来管控;

一、 .NET Framework2.0时代:出现了一个线程池ThreadPool

是一种池化思想,相当于是在池子中,有线程存在;如果需要使用线程;就可以直接到线程池中去获取直接使用,如果使用完毕,在自动的回放到线程池中去;

好处:

1.不需要程序员对线程的数量管控,提高性能,放置滥用

2.去掉了很多在Thread中没有必要的Api

二、线程池如何申请一个线程呢?

 ThreadPool.QueueUserWorkItem((state) =>
{
Debug.WriteLine($"***************ThreadPool start: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
Thread.Sleep(5000);
Debug.WriteLine($"***************ThreadPool end: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
});

三、线程等待

  1. 观望式的:
  2. 定义一个监听ManualResetEvent
  3. 通过ManualResetEvent.WaitOne等待
  4. 等到ManualResetEvent.Set方法执行,方法执行完毕后,主线程就继续往后;
            ManualResetEvent manualResetEvent = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem((state) =>
{
Debug.WriteLine($"***************ThreadPool start: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
Thread.Sleep(5000);
Debug.WriteLine($"***************ThreadPool end: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
manualResetEvent.Set();
});
manualResetEvent.WaitOne();
Debug.WriteLine($"***************Main Thread end: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
}

四、线程池如何控制线程数量

如果通过SetMinThreads/SetMaxThreads来设置线程的数量;这个数量访问是在当前进程是全局的;

 {
int workerThreads = 4;
int completionPortThreads = 4;
ThreadPool.SetMinThreads(workerThreads, completionPortThreads);
}
{
int workerThreads = 8;
int completionPortThreads = 8;
ThreadPool.SetMaxThreads(workerThreads, completionPortThreads);
}
{
ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads);
Debug.WriteLine($"当前进程最小的工作线程数量:{workerThreads}");
Debug.WriteLine($"当前进程最小的IO线程数量:{completionPortThreads}");
}
{
ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
Debug.WriteLine($"当前进程最大的工作线程数量:{workerThreads}");
Debug.WriteLine($"当前进程最大的IO线程数量:{completionPortThreads}");
}

Task

一、Task开启线程有哪些方式

Action action = () =>
{
Console.WriteLine($"***************Task start: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
Console.WriteLine("启动了一个新的线程");
Console.WriteLine($"***************Task end: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
}; Task task = new Task(action);
task.Start(); Task.Run (() =>
{
Console.WriteLine($"***************Task.Run start: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
Console.WriteLine("启动了一个新的线程");
Console.WriteLine($"***************Task.Run end: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
}); TaskFactory taskFactory = new TaskFactory();
taskFactory.StartNew(() =>
{
Console.WriteLine($"***************TaskFactory start: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
Console.WriteLine("启动了一个新的线程");
Console.WriteLine($"***************TaskFactory end: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
}); Task.Factory.StartNew(() =>
{
Console.WriteLine($"***************Task.Factory start: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
Console.WriteLine("启动了一个新的线程");
Console.WriteLine($"***************Task.Factory end: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
});

启动的多线程的特点:

  1. 不阻塞主线程----不会卡顿界面
  2. 线程的启动---由操作系统来调度启动; 延迟启动(延迟很短)
  3. 并发执行~~

线程执行完毕就销毁了吗?

ThreadPool 线程池----Task 线程都是来自于线程池的;

多进程技术的使用场景的分析

问题:尽可能的多启动线程?? 万万不可的,一定要适当的使用;

一堆业务逻辑: 项目要开发 10个板块

单线程执行: 一个人去承担这个项目开发----一步一步的做;一个版快一个板块的去开发; 开发周期时间长

多线程执行: 一个团队开发: 效率更高~~ 多个人可以分工开发;

类比: 一个人(开支小)和一个团队(10个人 10份工资);

线程等待

有Delay 和 Sleep两种方式来进行线程的等待.

{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Task.Delay(3000);
stopwatch.Stop();
Console.WriteLine($"time:{stopwatch.ElapsedMilliseconds}");
} {
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Thread.Sleep(3000);
stopwatch.Stop();
Console.WriteLine($"time:{stopwatch.ElapsedMilliseconds}");
}

结果为:

Task.Delay().ContinueWith() 不阻塞主线程,等待多长时间之后,可以执行一段业务逻辑----回调函数

Thread.Sleep() 阻塞主线程,主线程等待指定时间后再运行。

线程等待的多种方案

  Task<int> task = Task.Run(() =>
{
Thread.Sleep(3000); Console.WriteLine("Open new thread!"); return 10;
}); int num = task.Result; //等待task执行完毕,获取返回值,会阻塞当前线程 //下面是没有返回值方法调用的时候,使用的方法
//Task.WaitAll(task); //等待task执行完毕,会阻塞当前线程 //int i = Task.WaitAny(task); //等待task执行完毕,会阻塞当前线程

什么场景下可以使用多线程呢?(可以并发的时候) 不适合使用多线程??

故事: 高级班的项目实战---逐个讲解知识点,然后项目实战,分工合作,分小组开发;

  1. 逐个讲解知识点 -----可以多线程来模拟?---只有Richard老师一个人讲解----不可用;不能并发,不能多线程来模拟
  2. 项目实战,分工合作,分小组开发; -----可以多线程来模拟?---有多个人同时开发,可以分工并发开发,可以多线程开发~~

模拟的代码

 /// <summary>
/// 模拟讲课的方法
/// </summary>
/// <param name="lesson">课程名</param>
private void Tech(string lesson)
{
Console.WriteLine($"{lesson} ||开始了.....");
long iResult = 0; for (int i = 0;i<1_000_000_000;i++)
{
iResult += i;
}
Console.WriteLine($"{lesson} ||讲完了.....");
}
/// <summary>
/// 模拟不同人开发的方法
/// </summary>
/// <param name="name"></param>
/// <param name="projectName"></param>
private void Coding(string name,string projectName)
{
Console.WriteLine($"************************* Coding Start || {name} {projectName} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ****************");
long iResult = 0; for (int i = 0; i < 1_000_000_000; i++)
{
iResult += i;
}
Console.WriteLine($"************************* Coding End || {name} {projectName} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ****************");
}

基础的项目流程

private void button5_Click(object sender, EventArgs e)
{
Console.WriteLine("同学们!开始上课了");
Tech("泛型");
Tech("委托");
Tech("多线程");
Tech("异步编程");
Tech("并发编程");
Console.WriteLine("知识点讲解完毕了~~开始项目实战开发`~~~");
TaskFactory factory = new TaskFactory(); factory.StartNew(() => Coding("张三", "数据库设计"));
factory.StartNew(() => Coding("李四", "框架的搭建"));
factory.StartNew(() => Coding("王五", "Wechat Pay"));
factory.StartNew(() => Coding("赵六", "Web Api"));
factory.StartNew(() => Coding("田七", "封装通用的组件"));
factory.StartNew(() => Coding("刘八", "编译"));
factory.StartNew(() => Coding("杨九", "发行")); }

需求一、所有人的任务都执行完成后,小聚一下,大吃一顿```

  private void button5_Click(object sender, EventArgs e)
{ List<Task> tasks = new List<Task>();
Console.WriteLine("同学们!开始上课了");
Tech("泛型");
Tech("委托");
Tech("多线程");
Tech("异步编程");
Tech("并发编程");
Console.WriteLine("知识点讲解完毕了~~开始项目实战开发`~~~");
TaskFactory factory = new TaskFactory(); tasks.Add( factory.StartNew(() => Coding("张三", "数据库设计")));
tasks.Add(factory.StartNew(() => Coding("李四", "框架的搭建")));
tasks.Add(factory.StartNew(() => Coding("王五", "Wechat Pay")));
tasks.Add(factory.StartNew(() => Coding("赵六", "Web Api")));
tasks.Add(factory.StartNew(() => Coding("田七", "封装通用的组件")));
tasks.Add(factory.StartNew(() => Coding("刘八", "编译")));
tasks.Add(factory.StartNew(() => Coding("杨九", "发行"))); Task.WaitAll(tasks.ToArray());
Console.WriteLine("项目开发完毕了~~~,去大吃一顿~~");
}

需求2、 开发人员中,只要其中有一个执行完成了,Richard老师就准备发布环境,准备发布部署

 private void button5_Click(object sender, EventArgs e)
{ List<Task> tasks = new List<Task>();
Console.WriteLine("同学们!开始上课了");
Tech("泛型");
Tech("委托");
Tech("多线程");
Tech("异步编程");
Tech("并发编程");
Console.WriteLine("知识点讲解完毕了~~开始项目实战开发`~~~");
TaskFactory factory = new TaskFactory(); tasks.Add( factory.StartNew(() => Coding("张三", "数据库设计")));
tasks.Add(factory.StartNew(() => Coding("李四", "框架的搭建")));
tasks.Add(factory.StartNew(() => Coding("王五", "Wechat Pay")));
tasks.Add(factory.StartNew(() => Coding("赵六", "Web Api")));
tasks.Add(factory.StartNew(() => Coding("田七", "封装通用的组件")));
tasks.Add(factory.StartNew(() => Coding("刘八", "编译")));
tasks.Add(factory.StartNew(() => Coding("杨九", "发行")));
{
Task.WaitAny(tasks.ToArray()); //等待一堆任务中,其中有一个执行完成了,继续往后执行~
Console.WriteLine("XXX 完成了开发任务~~,Richard老师就准备发布环境,准备发布部署");
}
{
Task.WaitAll(tasks.ToArray());
Console.WriteLine("项目开发完毕了~~~,去大吃一顿~~");
} }

使用场景:

Task.WaitAll----系统首页---包含了很多的信息---都是后台提供----获取这个结果的时候;准备一个复杂实体---包含各种信息 查询这些数据---可以多线程去执行;同时查询;

查询必须要获取到所有的数据----要获取所有的数据----Task.WaitAll

Task.WaitAny----查询一条数据----数据来源可能是不同的地方,数据库/缓存/接口/读取硬盘中的数据

1.传统做法: 先查询缓存试试看,如果没有,再查询数据库,如果没有,再继续往后,直到查询到数据为止;

2.有四个渠道获取数据----> 只要有一个渠道获取到数据就Ok, 直接启动四个线程去查询; 等待其中有一个线程执行完成,特殊处理,如果查询到数据后,就结束~~ 只要有一个执行结束了,就已经拿到数据了,其他的不用管了~~

需求3、 有没有可以不阻塞主线程,也能达到效果;

 private void button2_Click(object sender, EventArgs e)
{
List<Task> tasks = new List<Task>();
Console.WriteLine("同学们!开始上课了");
Tech("泛型");
Tech("委托");
Tech("多线程");
Tech("异步编程");
Tech("并发编程");
Console.WriteLine("知识点讲解完毕了~~开始项目实战开发`~~~");
TaskFactory factory = new TaskFactory(); tasks.Add(factory.StartNew(Object => Coding("张三", "数据库设计"),"张三"));
tasks.Add(factory.StartNew(Object => Coding("李四", "框架的搭建"), "李四"));
tasks.Add(factory.StartNew(Object => Coding("王五", "Wechat Pay"), "王五"));
tasks.Add(factory.StartNew(Object => Coding("赵六", "Web Api"), "赵六"));
tasks.Add(factory.StartNew(Object => Coding("田七", "封装通用的组件"), "田七"));
tasks.Add(factory.StartNew(Object => Coding("刘八", "编译"), "刘八"));
tasks.Add(factory.StartNew(Object => Coding("杨九", "发行"), "杨九")); {
factory.ContinueWhenAny(tasks.ToArray(), (task) =>
{
Console.WriteLine($"{task.AsyncState} 完成了开发任务~~,发一个小红包");
});
}
{
factory.ContinueWhenAll(tasks.ToArray(), (task) =>
{
Console.WriteLine("项目开发完毕了~~~,去大吃一顿~~");
}); }
}

需求4、如果想要完成以上需求,要求不阻塞主线程,如果也没有ContinueWhenAll api.


Task.Run(() =>
{
Task.WaitAll(tasks.ToArray());
Console.WriteLine("项目开发完毕了~~~,去大吃一顿~~");
});

通过Task返回一个字符串

{
List<Task<string>> tasklist = new List<Task<string>>();
for (int i = 0; i < 3; i++)
{
string k = $"{i}";
tasklist.Add(Task.Run(() =>
{
return $"{k}_Task";
}));
}
Task.Run(() =>
{
Task.WaitAny(tasklist.ToArray());
Task<string> task = tasklist.First(c => c.Status == TaskStatus.RanToCompletion);
Console.WriteLine(task.Result);
});
}

Paralell

如何批量开启10个线程?

 Parallel.For(0, 10, (i) =>
{ Console.WriteLine($"Thread id : {Thread.CurrentThread.ManagedThreadId.ToString("00") }") ; });

如何控制启动线程的数量?

 ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 10; Parallel.For(0, 10100, parallelOptions, (i) =>
{
Console.WriteLine($"Thread id : {Thread.CurrentThread.ManagedThreadId.ToString("00") }"); });

线程异常处理

1.try_catch捕获不到多线程内部的异常.

按照正常的Try Catch来处理异常。

try {
for (int i = 0; i < 20; i++)
{
string str = $"Advance_{i}";
Task.Run(() =>
{
if (str.Equals("Advance_7"))
{
throw new Exception("Advance_7异常");
}
else if (str.Equals("Advance_10"))
{
throw new Exception("Advance_{10}异常");
}
else if (str.Equals("Advance_15"))
{
throw new Exception("Advance_15异常");
}
else if (str.Equals("Advance_18"))
{
throw new Exception("Advance_18异常");
}
else
{
Console.WriteLine(str);
}
}); }
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}

2.如何捕捉线程内部的异常,try-catch 包裹,线程等待; 可以捕捉到AggregateException类型的异常;

3.一个try可以对应多个catch 发生异常后,catch捕捉,是从上往下匹配异常类型,只要是匹配到异常类型后,就进入开始处理异常;

4.如何输出消息, 要转换成AggregateException,获取InnerExceptions 的集合,多线程发生的多个异常,都在这个集合中;

  private void button4_Click(object sender, EventArgs e)
{ List<Task> tasks = new List<Task>();
try {
for (int i = 0; i < 20; i++)
{
string str = $"Advance_{i}";
Task task = Task.Run(() =>
{
if (str.Equals("Advance_7"))
{
throw new Exception("Advance_7异常");
}
else if (str.Equals("Advance_10"))
{
throw new Exception("Advance_{10}异常");
}
else if (str.Equals("Advance_15"))
{
throw new Exception("Advance_15异常");
}
else if (str.Equals("Advance_18"))
{
throw new Exception("Advance_18异常");
}
else
{
Console.WriteLine(str);
}
});
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
} }

线程取消

有一个需求:

首页---数据块---考情/周top10/月top ......

启动四个线程去获取数据,要正常展示----一定要四个线程都能正常获取到数据,必然要等待四个线程都执行结束;

场景:四个线程,有某一个线程异常了~~ 整块数据不能用; 如果有异常,其他的正常的线程,其实查询也没有价值,既然没有异常的线程执行也没价值,就应该取消-----(因为线程在执行业务逻辑---需要消耗计算机的资源,计算机的资源是有限的)

标准方案:

定义一个cts,包含一个IsCancellationRequested 属性,默认值为=false,同时提供了一个Cancel方法, IsCancellationRequested: 默认的false ----true; IsCancellationRequested 属性 只能通过Cancel来变化,不能通过其他的渠道修改;

 CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

 try {
for (int i = 0; i < 50; i++)
{
string str = $"Advance_{i}";
Task.Run(() =>
{
if (cancellationTokenSource.IsCancellationRequested == false)
{
Console.WriteLine("正常运行"); if (str.Equals("Advance_7"))
{
cancellationTokenSource.Cancel();
throw new Exception("Advance_7异常");
}
else if (str.Equals("Advance_10"))
{
cancellationTokenSource.Cancel();
throw new Exception("Advance_{10}异常");
}
else if (str.Equals("Advance_15"))
{
cancellationTokenSource.Cancel();
throw new Exception("Advance_15异常");
}
else if (str.Equals("Advance_18"))
{
cancellationTokenSource.Cancel();
throw new Exception("Advance_18异常");
} }
else
{
Console.WriteLine("线程非正常退出");
}
}); } }
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}

多线程的中间变量

先看一段代码

for (int i = 0; i < 10000; i++)
{
Task.Run(() => Console.WriteLine($"{i}"));
}

输出的都是10000

为什么会这样那:

int i = 0; 开始循环,定义好的一个变量;

线程是延迟启动,启动线程不阻塞UI线程; 多线程要执行逻辑,要使用i,i已经是20了;

要实现我们的目的

 for (int i = 0; i < 10000; i++)
{
int k = i; Task.Run(() => Console.WriteLine($"{k}"));
}

线程安全

线程不安全:多线程在执行业务逻辑的时候,得到的结果,如果和单线程执行的结果如果不一致,那就是线程不安全~~

线程安全:单线程执行的结果要和多线程执行的结果要一致;线程安全的;

有多线程不安全的代码

  private void button6_Click(object sender, EventArgs e)
{
List<int> list = new List<int>();
List<Task> tasks = new List<Task>();
for (int i = 0; i < 10000; i++)
{
tasks.Add(Task.Run(() => { list.Add(i); }));
} Task.WaitAll(tasks.ToArray());
Console.WriteLine(list.Count);
}

如何解决线程安全呢?

  1. 锁, ----控制执行的线程只能有一个
  2. 直接使用单线程;
  3. 使用线程安全对象 看看数据结构 线程安全对象 List/Arraylist 都不是线程安全的集合--把list Arraylist 换成安全对象;
  4. 通过算法+拆分做到---划块操作数据; 原理:还是单线程去操作一块数据;
 private readonly static object obj = new object();
private void button6_Click(object sender, EventArgs e)
{
List<int> list = new List<int>();
List<Task> tasks = new List<Task>();
for (int i = 0; i < 10000; i++)
{ tasks.Add(Task.Run(() =>
{
//锁: 控制锁内部的代码执行,只能有一个线程进入执行,必须要等进入锁的线程执行结束了,其他的线程才能再进去一个; 反多线程;
lock (obj)
{
list.Add(i);
}
})); } Task.WaitAll(tasks.ToArray());
Console.WriteLine(list.Count);
}

Csharp线程的更多相关文章

  1. EF Core使用SQL调用返回其他类型的查询 ASP.NET Core 2.0 使用NLog实现日志记录 CSS 3D transforms cSharp:use Activator.CreateInstance with an Interface? SqlHelper DBHelper C# Thread.Abort方法真的让线程停止了吗? 注意!你的Thread.Abort方法真

    EF Core使用SQL调用返回其他类型的查询   假设你想要 SQL 本身编写,而不使用 LINQ. 需要运行 SQL 查询中返回实体对象之外的内容. 在 EF Core 中,执行该操作的另一种方法 ...

  2. CSharp 相关知识点小结

    1.JS获取iframe下面的内容document.getElementById('IFRAME1').contentDocument; 2.dialog 弹出层,定位:postion:'bottom ...

  3. WPF [调用线程无法访问此对象,因为另一个线程拥有该对象。] 解决方案以及如何实现字体颜色的渐变

    本文说明WPF [调用线程无法访问此对象,因为另一个线程拥有该对象.] 解决方案以及如何实现字体颜色的渐变 先来看看C#中Timer的简单说明,你想必猜到实现需要用到Timer的相关知识了吧. C# ...

  4. 不支持一个 STA 线程上针对多个句柄的 WaitAll

    [csharp] view plaincopy using System; using System.Collections.Generic; using System.Windows.Forms; ...

  5. AnimateWindow 阻塞当前线程问题

    今天查了蛮多的,虽然不是系统的学习,收获也不小.下面说一下我的解决方法: 问题一:采用 AnimateWindow API实现消息窗体FormMsg的动画出现,但由于该方法会阻塞当前线程,造成其他窗体 ...

  6. Csharp Winfrom 多串口通信

    Csharp 多串口通信 顾名思义,多串口通信,普通的PC机一般只有一个串口,现在很多家用的PC都没有串口,那么问题来了,如何保证多串口呢? 有一种神器,MOXA CP-168U Series PCI ...

  7. ThreadPool(线程池) in .Net

    本文来自:http://rickie.cnblogs.com/archive/2004/11/23/67275.html 在多线程的程序中,经常会出现两种情况.一种情况下,应用程序中的线程把大部分的时 ...

  8. C++多线程编程(三)线程间通信

    多线程编程之三——线程间通讯 作者:韩耀旭 原文地址:http://www.vckbase.com/document/viewdoc/?id=1707 七.线程间通讯 一般而言,应用程序中的一个次要线 ...

  9. C#读书笔记:线程,任务和同步

    前言 学习C#两个多月了,像当初实习做PHP开发一样,也是由着一个个Feature需求,慢慢掌握了很多相关的编程技巧.本次主要记录下学习C# 多线程的相关知识. 参考书籍:<Csharp高级编程 ...

  10. C# 线程 在 sleep,suspend 之后 Abort 的方法

    1) 线程在sleep时的Abort     方法:对线程函数用 catch ThreadAbortException ,并return.   示例: [csharp] view plaincopy ...

随机推荐

  1. WriteFile 奇怪的现象

    项目中有个需求是要对文本内容检索并重写,我们使用的是 WriteFile 覆盖旧的文本内容 最小示例: #include <Windows.h> #include <iostream ...

  2. 细说Spring框架之核心01-概述

    官网:https://spring.io/projects/spring-framework 文档:https://docs.spring.io/spring-framework/docs/curre ...

  3. 问题:django中对datetime类型数据在pycharm中sqlite3进行修改时,修改后datetime日期数据变成了时间戳类型

    这是正在修改的 提交完之后 问题原因 问题原因是sqlite数据库对日期类型不敏感,Pycharm直接插入会变成图中这样的时间戳,用POST请求添加数据或Django自带的后台管理插入不会有这样的问题 ...

  4. 【Azure Redis】Redis客户端出现15分钟的超时异常

    问题描述 客户端使用 Lettuce.io 连接 Azure Redis,出现了长达15分钟的Timeout异常. 问题解答 Azure Redis作为PaaS服务,由于一些平台的升级操作而引发的故障 ...

  5. 【Azure 存储服务】MP4视频放在Azure的Blob里面,用生成URL在浏览器中打开之后,视频可以正常播放却无法拖拽视频的进度

    问题描述 把MP4视频放在Azure的Blob里面,用生成URL在浏览器中打开之后,视频可以正常播放却无法拖拽视频的进度,这是什么情况呢? 问题解答 因为MP4上传到Azure Blob后,根据公开的 ...

  6. 【Azure 应用服务】在App Service 中如何通过Managed Identity获取访问Azure资源的Token呢? 如Key Vault

    问题描述 当App Service启用了Managed Identity后,Azure中的资源就可以使用此Identity访问. 如果需要显示的获取这个Token,如何实现呢? 问题解答 在App S ...

  7. 【Azure 事件中心】Event Hub Client 连接超时(OperationTimeout)测试及解说

    Azure Event Hub(Azure事件中心) 是大数据流式处理平台和事件引入服务. 它可以每秒接收和处理数百万个事件.在我们的使用中,需要代码编写的是两个部分:事件生产者和事件接收者 事件生成 ...

  8. 【Azure Redis 缓存】Azure Cache for Redis 服务的导出RDB文件无法在自建的Redis服务中导入

    问题描述 使用微软云的Redis服务,导出它的RDB文件后,想把数据恢复到本地自建的Redis服务中,发现出现如下错误: 15000:S 21 Jun 08:14:11.199 * Retrying ...

  9. C语言之牛必克拉斯 main() 函数

    C语言之main()函数 C程序最大的特点就是所有的程序都是用函数来装配的.main()称之为主函数,是所有程序运行的入口.其余函数分为有参或无参两种,均由main()函数或其它一般函数调用,若调用的 ...

  10. Redis之数据持久化小结

    一.概述 Redis作为内存型的数据库,虽然很快,依然有着很大的隐患,一旦服务器宕机重启,内存中数据还会存在吗? 很容易想到的一个方案是从后台数据恢复这些数据,如果数据量很小,这倒是一个可行的方案.但 ...