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

目前的做法:把我的请求放入一个容器内,然后待到某一条件,就从这个容器把请求发送出去,等客户返回信息时,查询容器中对应请求中特定的事件。开始的时候我使用 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. Ajax 如何执行 Response.Redirect

    Ajax 直接对服务端的Response.Redirect是不感冒的, 另觅途径, 具体可行办法如下: Web Service 服务端: public WXService() { if (!IsVal ...

  2. Linux系统忘记管理员密码(CentOS、RHEL、Ubuntu)

    Linux系统忘记管理员密码(CentOS.RHEL.Ubuntu) 系统使用过程中,尤其是生产环境中.万一忘记管理员密码,该怎么办?是不是很绝望? 1.RHEL 7.0 重启主机进入引导界面键入e键 ...

  3. kvm虚拟化之kvm虚拟机克隆

    kvm虚拟机的克隆分为两种情况,本文也就通过以下两种情况进行克隆,克隆虚拟机为OEL5.8X64. (1) KVM主机本机虚拟机直接克隆. (2) 通过复制配置文件与磁盘文件的虚拟机复制克隆(适用于异 ...

  4. 9、select 语句

    基础语句 select * from tb1; select * from tb1 limit 3; select name,age from tb1; select name,age from tb ...

  5. “全栈2019”Java第六十二章:接口与常量详解

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

  6. Python 中的 10 个常见安全漏洞,以及如何避免(下)

    简评:编写安全代码很困难,当你学习一个编程语言.模块或框架时,你会学习其使用方法. 在考虑安全性时,你需要考虑如何避免被滥用,Python 也不例外,即使在标准库中,也存在用于编写应用的不良实践.然而 ...

  7. 3.2、Factorization Machine实践

    1.在上一篇博客中我们构建度为二的因子分解机模型,这篇博客对这个模型进行实践 下图为准备的数据集: 完整代码为: # -*- coding: UTF-8 -*- # date:2018/6/6 # U ...

  8. 键盘压缩背景,ios滚动不流畅,禁止遮罩层下面内容滚动

    1.<!--防止软键盘压缩页面背景图片--> <script> const bodyHeight = document.documentElement.clientHeight ...

  9. PHP 五大运行模式

    查看当前php 运行模式 php_sapi_name(); 运行模式 关于PHP目前比较常见的五大运行模式: 1)CGI(通用网关接口/ Common Gateway Interface) 2)Fas ...

  10. 做point data的切面的时候的注意事项

    正确的顺序应该是: 先导入cell data,再转换为point data,再做切面.结果如下: 如果这里导入cell data以后先做了切面再转换为point data,结果就是这样的: 很明显中间 ...