业务需求: 发送特定的请求,根据返回的信息执行特定的事件。

目前的做法:把我的请求放入一个容器内,然后待到某一条件,就从这个容器把请求发送出去,等客户返回信息时,查询容器中对应请求中特定的事件。开始的时候我使用 List .其中遇到一些问题,纪录一下

  1 namespace CollSecExp
2 {
3 class Program
4 {
5 static void Main(string[] args)
6 {
7 List<int> list = new List<int>();
8 for (int i = 0; i < 10; i++)
9 {
10 list.Add(i);
11 }
12
13 Thread t1 = new Thread(() =>
14 {
15 foreach (var item in list)
16 {
17 Console.WriteLine("t1.item:{0}", item);
18 Thread.Sleep(1000);
19 }
20 });
21 t1.Start();
22
23 Thread t2 = new Thread(() =>
24 {
25 Thread.Sleep(1000);
26 list.RemoveAt(1);
27 list.RemoveAt(3);
28 foreach (var item in list)
29 {
30 Console.WriteLine("t2.item:{0}", item);
31 }
32 });
33 t2.Start();
34 }
35 }
36 }

运行会抛出InvalidOperationException异常,提示“集合已修改;可能无法执行枚举操作。”

这是因为,线程2移除index=1,3的元素导致集合被修改。使用加锁

  1 namespace CollSecExp
2 {
3 class Program
4 {
5 static void Main(string[] args)
6 {
7 object sycObj = new object();
8 List<int> list = new List<int>();
9 for (int i = 0; i < 10; i++)
10 {
11 list.Add(i);
12 }
13
14 Thread t1 = new Thread(() =>
15 {
16 lock (sycObj)
17 {
18 foreach (var item in list)
19 {
20 Console.WriteLine("t1.item:{0}", item);
21 Thread.Sleep(1000);
22 }
23 }
24 });
25 t1.Start();
26
27 Thread t2 = new Thread(() =>
28 {
29 Thread.Sleep(1000);
30 lock (sycObj)
31 {
32 list.RemoveAt(1);
33 list.RemoveAt(3);
34 foreach (var item in list)
35 {
36 Console.WriteLine("t2.item:{0}", item);
37 }
38 }
39 });
40 t2.Start();
41 }
42 }
43 }

加锁就可以解决了。

后来使用了想起了对于请求,我们可以使用队列,最简单的请求Queue如下

  1 static void Main(string[] args)
2 {
3 int count = 0;
4 Queue<int> queue = new Queue<int>();
5 Task.Factory.StartNew(() =>
6 {
7 while (true)
8 {
9 Thread.Sleep(3000);
10 Console.WriteLine("生产的元素是: " + count);
11 queue.Enqueue(count);
12 count++;
13
14 }
15
16 });
17
18 ;
19 Task.Factory.StartNew(() =>
20 {
21 while (true)
22 {
23 if (queue.Count > 0)
24 {
25 int value = queue.Dequeue();
26 Console.WriteLine("worker1 " + ": 消费的元素是: " + value);
27 }
28 }
29
30 });
31
32 Task.Factory.StartNew(() =>
33 {
34 while (true)
35 {
36 if (queue.Count > 0)
37 {
38 int value = queue.Dequeue();
39 Console.WriteLine(Thread.CurrentThread + " : 消费的元素是: " + value);
40 }
41 }
42
43 });
44
45
46 Console.ReadKey();
47
48 }

由于没有加锁或是其他机制,那么很容易出错,于是改进了版本。

  1         static void Main(string[] args)
2 {
3 int count = 0;
4 var queue = new ConcurrentQueue<int>();
5 Task.Factory.StartNew(() =>
6 {
7 while (true)
8 {
9 Thread.Sleep(1000);
10 Console.WriteLine("生产的元素是: " + count);
11 queue.Enqueue(count);
12 count++;
13
14 }
15
16 });
17
18 ;
19 Task.Factory.StartNew(() =>
20 {
21 while (true)
22 {
23
24 int value;
25 if (queue.TryDequeue(out value))
26 {
27 Console.WriteLine("worker1 " + ": 消费的元素是: " + value);
28 }
29
30 }
31
32 });
33
34 Task.Factory.StartNew(() =>
35 {
36 while (true)
37 {
38 int value;
39 if (queue.TryDequeue(out value))
40 {
41 Console.WriteLine("worker2 " + " : 消费的元素是: " + value);
42 }
43 }
44
45 });

执行这段代码,可以工作,但是有点不太优雅,能不能不要去判断集合是否为空?集合当自己没有元素的时候自己Block一下可以吗?答案当然是可以的,使用BlockingCollection即可:

  1         static void Main(string[] args)
2 {
3 int count = 0;
4 var queue = new BlockingCollection<int>();
5 var product = Task.Factory.StartNew(() =>
6 {
7 for (int i = 20 - 1; i >= 0; i--)
8 {
9 Console.WriteLine("生产的元素是: " + count);
10 queue.Add(count);
11 count++;
12 }
13
14 });
15
16
17 var consumer1 = Task.Factory.StartNew(() =>
18 {
19 foreach (int value in queue.GetConsumingEnumerable())
20 {
21 Console.WriteLine("worker1 " + ": 消费的元素是: " + value);
22
23 }
24
25 });
26
27 var consumer2 = Task.Factory.StartNew(() =>
28 {
29 foreach (int value in queue.GetConsumingEnumerable())
30 {
31 if (value % 2 == 0)
32 {
33 Console.WriteLine("worker2 " + " : 消费的元素是: " + value);
34 }
35 else
36 {
37 queue.Add(value);
38 }
39 }
40 });
41 Task.WaitAny(product, consumer2, consumer1);
42 Console.ReadKey();
43 }

当我需要控制Queue停止反复遍历获取queue是否存在元素时,可以使用如下方法,同时在构造方法中可以指定队列的大小

  1 blockingCollection.CompleteAdding();

参考文章:

C#的变迁史 - C# 4.0 之线程安全集合篇

List

List和Queue使用过程中的纪录的更多相关文章

  1. 使用beanstalkd实现定制化持续集成过程中pipeline

    持续集成是一种项目管理和流程模型,依赖于团队中各个角色的配合.各个角色的意识和配合不是一朝一夕能练就的,我们的工作只是提供一种方案和能力,这就是持续集成能力的服务化.而在做持续集成能力服务化的过程中, ...

  2. zabbix 3.0.3 (nginx)安装过程中的问题排错记录

    特殊注明:安装zabbix 2.4.8和2.4.6遇到2个问题,如下:找了很多解决办法,实在无解,只能换版本,尝试换(2.2.2正常 | 3.0.3正常)都正常,最后决定换3.0.3 1.Error ...

  3. 最新cocoapods安装流程,安装过程中遇到的问题及解决方法

    最近重新安装了一次cocoapods,参考的安装流程:http://blog.csdn.net/showhilllee/article/details/38398119/ 但是现在的cocoapods ...

  4. ios逆向过程中lldb调试技巧

    在ios逆向过程中,善于运用lldb,会给逆向带来很大的方便 一般的命令: 1.image list -o -f  看看各个模块在内存中的基址 2.register read r0  读取寄存器r0的 ...

  5. 使用 VSTS 进行 CI 的过程中,无法识别 .NET Core 2.x 的情况处理

    大概是由于 .NET Core 2.1 还没有正式发布,使用 VSTS 进行持续集成(CI)的过程中,自动 Build 的环节无法识别 .NET Core 2.1 的框架,查看日志会提示如下错误: V ...

  6. sharepoint环境安装过程中几点需要注意的地方

    写在前面 上篇文章也说明了,在安装sharepoint环境的时候,确实吃了不少苦头,这里纪录一下安装过程中遇到的几个问题. 安装环境 windows server 2012 r2 standard x ...

  7. QTcpSocket使用过程中的一些问题记录

    目前,在将原来C的socket通讯改为使用Qt类库QTcpSocket通讯,在修改过程中遇到不少问题,在此将问题一并记录,以备后面使用. 采用的通讯方式:QTimer定时器.QThread多线程和QT ...

  8. CASE:DB shutdown/open 过程中发生异常导致JOB不能自动执行

    CASE:DB shutdown/open 过程中发生异常导致JOB不能自动执行 现象: 一个DB中的所有JOB在3月25日之后就不再自动运行,查询DBA_JOBS,发现LAST_DATE定格在3月2 ...

  9. 在对listctrl的控件进行重载的过程中,GetHeaderCtrl()返回NULL的问题

    先谈谈我的问题吧! 在使用listctrl的过程中,我需要在列表头部添加checkbox,实现全选的功能. 经过网上资料的罗列,我找到了一个demo,使用的重绘的方法,在使用的过程中,我发现我的列表头 ...

随机推荐

  1. cisco和h3c网络设备中一次性打印全部配置信息

    cisco的是全页打印配置信息的命令: #terminal length 0 #show run 华为和h3c的是: >screen-length 0 temporary >display ...

  2. IO模型《五》异步IO

    Linux下的asynchronous IO其实用得不多,从内核2.6版本才开始引入.先看一下它的流程: 用户进程发起read操作之后,立刻就可以开始去做其它的事.而另一方面,从kernel的角度,当 ...

  3. Oracle ocp 12c-071最新考试题库及答案-1

    choose the best answer: View the Exhibit and examine the structure of the CUSTOMERS table. CUSTOMER_ ...

  4. “全栈2019”Java第十七章:赋值运算符和算术运算符

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  5. 使用 Git 进行版本控制

    使用 Git 进行版本控制 版本控制软件让你能够拍摄处于可行状态的项目的快照.修改项目(如实现新功能)后,如果项目不能正常运行,可恢复到前一个可行状态. 通过使用版本控制软件,你可以无忧无虑地改进项目 ...

  6. 洛谷P3706 [SDOI2017]硬币游戏(概率生成函数+高斯消元)

    题面 传送门 题解 不知道概率生成函数是什么的可以看看这篇文章,题解也在里面了 //minamoto #include<bits/stdc++.h> #define R register ...

  7. [转] HTTP状态码错误代码

    一些常见的状态码为: 200 - 服务器成功返回网页 404 - 请求的网页不存在 503 - 服务不可用 详细分解: 1xx(临时响应) 表示临时响应并需要请求者继续执行操作的状态代码. 代码 说明 ...

  8. django2使用xadmin打造适合国人的后台管理系统(1)

    python火了之后,学习python的人也越来越多了,python做web开发的话,flask.django是比较火的框架了,django是一个比较大的框架,也是一个快速开发利器.但是,django ...

  9. 大数据list去重

    MaxList模块主要是对Java集合大数据去重的相关介绍. 背景: 最近在项目中遇到了List集合中的数据要去重,大概一个2500万的数据,开始存储在List中,需要跟一个2万的List去去重. 直 ...

  10. c调用 lua 栈操作

    转自https://www.cnblogs.com/ringofthec/archive/2010/10/22/lua.html 打算记录一些lua_api, 可能会觉得lua文档中已经说的很清楚了, ...