赞
踩
老铁
先不废话,简单粗暴,直接来源码:
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.Threading;
- using System.Threading.Tasks;
- using System.Windows.Forms;
-
-
- namespace TestConsole
- {
- class Program
- {
- private static ManualResetEvent manualResetEvent = new ManualResetEvent(false);//AutoResetEvent略去
- //private static List<int> list = new List<int>();//非线程安全,“集合已修改;可能无法执行枚举操作。”
- private static ConcurrentBag<int> list = new ConcurrentBag<int>();
- private static CancellationTokenSource cancellationToken = new CancellationTokenSource();
- private static ConcurrentDictionary<int, string> concurrentDictionary = new ConcurrentDictionary<int, string>();
- private static ConcurrentQueue<int> concurrentQueue = new ConcurrentQueue<int>();
- static void Main(string[] args)
- {
- try
- {
- //ThreadSafetyTest();
- //ManualResetEventHandler();
- //DelegateTest();
- TasKTest();
-
-
- Console.ReadKey();
- }
- catch (Exception ex)
- {
- MessageBox.Show(ex.StackTrace + " " + ex.Message);
- }
- }
- #region TASK用法举例
- static void TasKTest()
- {
- //带返回值的task
- Task<String> mytask = Task.Run(async () =>
- {
- await Task.Delay(5000);
- return "老铁666";
- }, cancellationToken.Token);
- //cancellationToken.Cancel();
- while (!mytask.Wait(100)) //在指定时间执行完成就返回true
- {
- Console.Write(".");
- }
- Console.WriteLine(mytask.Result);//task任务取消,所以获取task.Result会报错
-
-
- Task taskFactory = Task.Factory.StartNew(() =>
- {
- Thread.Sleep(1000);
- Console.WriteLine("perfect!");
- });
- Console.WriteLine(taskFactory.Status);
- taskFactory.Wait();//taskFactory任务完成才往下执行
- Task.WaitAll(mytask, taskFactory);//所有任务完成再往下执行
- Task.WaitAny(mytask, taskFactory);//任一任务完成就往下执行
- Console.WriteLine(taskFactory.Status);
-
-
- Task taskTest = taskFactory.ContinueWith(DoOnSecond);//ContinueWith用法举例
- }
- static void DoOnSecond(Task t)
- {
- Console.WriteLine("task {0} finished", t.Id);
- Console.WriteLine("this task id {0}", Task.CurrentId);
- Console.WriteLine("do some cleanup");
- }
- #endregion
-
-
- #region ManualResetEvent 用法举例
- private static void ManualResetEventHandler()
- {
- Thread t1 = new Thread(Thread1);
- t1.Start();
- Thread t2 = new Thread(Thread2);
- t2.Start();
- Thread.Sleep(2000);
- manualResetEvent.Set();
- Thread.Sleep(5000);
- t1.Abort();//放弃执行线程t1
- Console.WriteLine(t1.ThreadState);
- Console.WriteLine(t2.ThreadState);
- }
- static void Thread1()
- {
- Console.WriteLine(manualResetEvent.WaitOne().ToString());//等待 manualResetEvent.Set()后执行
- manualResetEvent.Reset();// 将信号置为初始状态,即false
- //Console.WriteLine(manualResetEvent.WaitOne().ToString());//阻塞当前线程,让他等待着
- MessageBox.Show("Thread1 run end");
- Parallel.For(0, 100000000, item =>
- {
- Console.WriteLine(item.ToString());
- });
- }
-
-
- static void Thread2()
- {
- Console.WriteLine(manualResetEvent.WaitOne(10000).ToString());//10秒内收到 manualResetEvent.Set(),即true就往下执行,或者超过100秒,也往下执行,但是状态依然为false
- MessageBox.Show("Thread2 run end");
- }
- #endregion
-
-
- #region 线程安全之ConcurrentBag 验证
- private static void ThreadSafetyTest()
- {
- int count = 0;
- Task t = new Task(() =>
- {
- while (true)
- {
- Thread.Sleep(1000);
- count++;
- list.Add(count);
- }
- });
- t.Start();
-
-
- Task.Run(() =>
- {
- while (true)
- {
- foreach (var item in list)
- {
- Thread.Sleep(1000);
- Console.WriteLine($"{list.Count}");
- }
- }
- });
-
-
- }
- #endregion
-
-
- public static void DelegateTest()
- {
- Func<string, string> RetBook = new Func<string, string>(FuncBook);
- Console.WriteLine(RetBook("《平凡的世界》"));
-
-
- }
- public static string FuncBook(string BookName)
- {
- return BookName;
- }
-
-
-
-
- }
- }
上面代码逻辑要是全懂了,下面就可以跳过了,
没看懂的继续。。。。。。。
① 首先,执行第一个方法ThreadSafetyTest();
涉及知识点:
task的创建和使用;
foreach遍历
线程安全集合ConcurrentBag用法
修改这里后运行:
- ThreadSafetyTest();
- //ManualResetEventHandler();
- //DelegateTest();
- //TasKTest();
执行结果如下:
这个方法体的作用主要是开了两个线程,第一个线程每隔1秒向集合添加一个数,第二个线程每隔一秒,便利输出集合。在这里集合要是改成list的,即:
- private static ConcurrentBag<int> list = new ConcurrentBag<int>();
- 改成=>
- private static List<int> list = new List<int>();
运行过程会报错:
非线程安全,“集合已修改;可能无法执行枚举操作。”
原因解释:不管是读还是写,同一时刻只能做一件事情,要么读,要么写,多个线程对同一个集合进行读写操作,就难免会出现线程安全问题,当然你可以
用lock关键字,进行线程同步,但是性能并不是特别理想,然后我尝试使用SynchronizedList<T>来代替使用List<T>达到线程安全的目的。但是发现性能依旧糟糕,于是查看了SynchronizedList<T>的源代码,发现它就是简单的在List<T>提供的API的基础上加了lock,所以性能基本与list<T>方式相差无几。最后使用ConcurrentBag<T>类来实现,性能有很大的改观。
知识拓展:线程安全的队列和字典:
- ConcurrentDictionary<int, string> concurrentDictionary = new ConcurrentDictionary<int, string>();
- ConcurrentQueue<int> concurrentQueue = new ConcurrentQueue<int>();
② 接下来,执行第二个方法ManualResetEventHandler();
涉及知识点:
ManualResetEvent用法(和AutoResetEvent类似,区别自己去学习);
主要包括:
manualResetEvent.Reset(); manualResetEvent.Set();
manualResetEvent.WaitOne()和manualResetEvent.WaitOne(time)
Thread线程创建和使用;
重点解释:
Console.WriteLine(manualResetEvent.WaitOne(10000).ToString());10秒内收到 manualResetEvent.Set(),即true就往下执行,或者超过100秒,也往下执行,但是状态依然为false
最后的执行结果:
③ 接下来,执行第三个方法DelegateTest();
知识点:带参数,带返回值的委托Func的用法
运行结果:
④ 最后,执行第四个方法DelegateTest();
涉及知识点:
task的创建和使用前面已经提供了两种创建方式,这里又提供了新的方式Task.Factory.StartNew(() =>{});
取消task任务之CancellationTokenSource的用法;
task的线程管控方法Task..Wait(time),Task.WaitAll(), Task.WaitAny(),task.ContinueWith.
运行结果:
原创不易,用100+行代码串接这么多重要知识点更加不易。
老铁666
老铁到底溜不溜,在看转发走起来!
进技术交流群的,加小编微信zls20210502,切记备注:进群!
项目工程下载地址:
链接: https://pan.baidu.com/s/1MEm-CjQjrReP6jxyqpLjQA
提取码: wr3n
欢迎关注公众号: dotnet编程大全
技术群: 需要进技术群的添加小编微信mm1552923,备注:加群;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。