C#关于线程安全队列ConcurrentQueue 使用方法的问题。

干拉痞痞虾 2019-04-18 12:06:04

以往写程序中,使用多线程同时操作一个队列,为了防止资源竞争问题(对于资源竞争也不是很理解),对这个队列自己设置一把锁(lock机制),线程每次想去访问这个队列的时候,就去申请这把锁,申请到了,才有操作队列的权限。但是看网上一些技术贴后,说这种方法还是不太好,之后就查到了C#提供了线程安全的队列。

现在对于这个线程安全队列有以下几个问题:
(1)这个队列所谓的线程安全就是C#内部已经实习了锁的机制吗?不用人为去写锁了吗?
(2)这种机制,是不是能够实现 多个线程同时放队列放数据,一个队列从队列中区数据 ,实现的时候,是不是就不用人为定义lock锁了呢?但是我并没有查到使用线程安全队列时用不用加锁 大伙能给个例子吗?

我自己写一个测试用的,定义了公共的,静态的一个线程安全队。

class PublicDataArea
{

public static ConcurrentQueue<int> queue = new ConcurrentQueue<int>();

}



然后再定义了一个类,这个类的功能就是接受外部传来的数据,不停的让它自增,每次自增后的数据都放在这个队列里面,类的内部定义了一个线程,这个线程的工作就是来做数据的自增,然后放入队列里面。 这是代码:

class WorkGroup
{
private int data; //需要自增的数据

//类内部的线程,线程工作就是让数据自增,放入队列中
public Thread th;

//构造函数接受外部传来的 需要进行自增操作的数据
public WorkGroup(int orderNumber,int data)
{
this.data = data;

th = new Thread(WorkThread);
th.Name = "线程"+orderNumber.ToString()+"号";
th.IsBackground = true;
}

void WorkThread() //线程工作函数
{
while (true)
{
Work();
Thread.CurrentThread.Join(500);
}
}


void Work()
{
try
{

data += 2; //自增数据
//这里不清楚,多个线程同时往里面放数据的时候,不需要一些限制操作吗,还是说线程安全队列自身已经提供了。
PublicDataArea.queue.Enqueue(data); //放入队列

//输出一下哪个线程将数据放入了队列中,放入的时间 ,放入的什么数据
Console.WriteLine(th.Name+"数据放入队列中,放入时间:"+DateTime.Now.ToString()+" 放入的数据为:"+data);
Console.WriteLine();

}
catch
{
MessageBox.Show("数据错误");
}

}








在主函数中,定义了一个专门从这个队列中取数据并输出的线程,这里使用线程安全队列取数据的方式,是直接摘抄网上的资料,主函数类中的代码:


class Program
{
public int p;
void FetchData()
{
try
{
//我对这句话的理解就是TryDequeue()这个函数,当队列中有数据,就返回true,将对头数据放到p上, 无数据直接返 false
//所以我觉得这句话的意思,,就是当队列中有数据的时候,就把数据放入p中,无数据直接返回false
if ( PublicDataArea.queue.TryDequeue(out p))
{

Console.WriteLine("当前 数据为: "+p + " 当前时间为:"+DateTime.Now.ToString());
Console.WriteLine();
}
else
{
Console.WriteLine("数据为空");
Console.WriteLine();
}
}
catch
{
MessageBox.Show("取数据时发生错误");
}
}



public void Method()
{
while (true)
{
FetchData();
// Thread.CurrentThread.Join(600);
}
}



static void Main(string[] args)
{
Program mc = new Program();

Thread td = new Thread(mc.Method);//定义一个专门从队列中取数据的线程
td.IsBackground = true;
td.Start();


WorkGroup work;

for (int i = 0,j=1; i < 50; i++,j+=10) //i 为线程的标识 ,j为需要自增的数据 目的就是看看多个线程同时访问时,有哪些线程
//能够访问到
{
work = new WorkGroup(i, j);
work.th.IsBackground = true;
work.th.Start();

work = null;
}





Console.ReadLine();




}
}








}




问题1: 大家帮我看看,我写的代码 能不能实现我上面提的那种机制,但 自己总觉得哪里不对劲,,但是又找不到原因,,,

问题2:想请教一下大家,线程安全队列 究竟怎么在多线程的环境下使用,,,什么才是线程安全???




...全文
2208 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
bloodish 2019-04-19
  • 打赏
  • 举报
回复
建议BlockingCollection了解一下,用来支持生产者-消费者场景的需求。 默认的容器类型是ConcurrentQueue,但也可以自己指定容器类型。
  • 打赏
  • 举报
回复
引用 楼主 椰子仓 的回复:
问题2:想请教一下大家,线程安全队列 究竟怎么在多线程的环境下使用,,,什么才是线程安全???
如果你经常需要写多线程并发程序,你反而不需要这些。因为它跟你自己写一行 lock 来同步访问没有什么差别,而它显然是牺牲了灵活性来换得让你少写一行 lock 语句的。但是对于初学者,反而觉得高大上,就会觉得“这种 class 特别不一样”,好像吃日本泡面就是吃日本料理了。
  • 打赏
  • 举报
回复
引用 4 楼 椰子仓 的回复:
在以往的使用Queue<T> 中,若想在多线程的环境下,使用,必须的自己定义个Lock锁, 这个ConcurrentQueue 是不是就不用定义锁,使用的时候直接就是入队 出队即可???那些所谓的资源竞争的问题,,ConcurrentQueue里面已经设置好这种机制了吗
是的,就是不用你自己写 lock(abc) 这样一行代码了,替你写了。 这样,不管你怎样用它,你都享受不到并发多线程的特点,你实际上是用着多线程的语法而访问一个阻塞排队的对象操作接口。就好像明明有8车道,但是进入隧道时就变为2车道,这个山洞就是所谓的“线程安全对象”。所以能不用就不用它,但是假设你懒得写上一行 lock 语句但是不写会出现数据冲突,那么你就可以用它。
干拉痞痞虾 2019-04-19
  • 打赏
  • 举报
回复
引用 7 楼 wanghui0380的回复:
用就好,40年前没有电饭煲,俺们用柴火灶煮饭,所以那时候老人们说,要舀米汤。或者先煮半熟然后再蒸。 现在用电饭煲,你就别纠结,为啥跟40年前的书上写的不一样了。 一样一样的道理,有些东西人家帮你做了,你就别纠结了。用就好了。如果你要纠结,那就把电饭煲拆开看,看看人家为啥不“舀米汤”,一样如果你要纠结“锁”,那就把微软的源代码看看(微软现在的代码是开源的,就算开源,俺们自己ilspy反编译看也没人拦着)
🌚🌚🌚🌚👍👍👍👍
橘子皮... 2019-04-19
  • 打赏
  • 举报
回复
ConcurrentQueue 在特殊的情况下也不是线程安全的,其实自己加locker如果对性能要求不高的话还是合适的,微软的东西有他自己的坑
干拉痞痞虾 2019-04-19
  • 打赏
  • 举报
回复
引用 9 楼 以专业开发人员为伍 的回复:
[quote=引用 4 楼 椰子仓 的回复:] 就好像明明有8车道,但是进入隧道时就变为2车道,这个山洞就是所谓的“线程安全对象”。所以能不用就不用它,但是假设你懒得写上一行 lock 语句但是不写会出现数据冲突,那么你就可以用它。
你说的这个山洞,它存在的意义 就是让外围的8车道的车辆,有序不乱的进入隧道的2车道??它的作用就是控制和调度 ?? “所以能不用就不用它” 这句话又怎么讲?
干拉痞痞虾 2019-04-19
  • 打赏
  • 举报
回复
引用 10 楼 以专业开发人员为伍 的回复:
[quote=引用 楼主 椰子仓 的回复:] 问题2:想请教一下大家,线程安全队列 究竟怎么在多线程的环境下使用,,,什么才是线程安全???
如果你经常需要写多线程并发程序,你反而不需要这些。因为它跟你自己写一行 lock 来同步访问没有什么差别,而它显然是牺牲了灵活性来换得让你少写一行 lock 语句的。但是对于初学者,反而觉得高大上,就会觉得“这种 class 特别不一样”,好像吃日本泡面就是吃日本料理了。[/quote] 对,是觉得这种class特别高大上, 其实出现这种感觉的原因就是一个是需要自己手动提供机制,,另一个是微软本身提供好了, 总感觉微软提供的 要比自己提高的好 。 另一个是你说的“失去了灵活性换的来是我少些的一句lock” ,出现这样的原因,还是我对“线程安全队列” 的理解,,,不够深入 ???它真正所解决的不仅仅是所谓的资源竞争问题吗?
stherix 2019-04-18
  • 打赏
  • 举报
回复
你用普通的队列比如Queue<> 多线程同时操作的话,可能会造成数据错误,比如不同线程push,pop多个数据,会造成大小和数据紊乱 因为访问head坐标和访问值不是原子操作 比如if(list.Count>0) list[0].xxxx 在多线程下,就算if判断成功,后面取list[0]也可能出错 ConcurrentQueue就是解决了这种类似的问题
干拉痞痞虾 2019-04-18
  • 打赏
  • 举报
回复
见到网上很多帖子,都说 :高效的线程安全队列ConcurrentQueue 他们是如何定义“高效”这个意思呢?
干拉痞痞虾 2019-04-18
  • 打赏
  • 举报
回复
我觉得可能还不理解什么是资源竞争,,以及ConcurrentQueue<T> 如何解决资源竞争的??
wanghui0380 2019-04-18
  • 打赏
  • 举报
回复
用就好,40年前没有电饭煲,俺们用柴火灶煮饭,所以那时候老人们说,要舀米汤。或者先煮半熟然后再蒸。 现在用电饭煲,你就别纠结,为啥跟40年前的书上写的不一样了。 一样一样的道理,有些东西人家帮你做了,你就别纠结了。用就好了。如果你要纠结,那就把电饭煲拆开看,看看人家为啥不“舀米汤”,一样如果你要纠结“锁”,那就把微软的源代码看看(微软现在的代码是开源的,就算开源,俺们自己ilspy反编译看也没人拦着)
干拉痞痞虾 2019-04-18
  • 打赏
  • 举报
回复
引用 5 楼 xian_wwq 的回复:
https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentqueue-1?redirectedfrom=MSDN&view=netframework-4.7.2 微软的文档说的挺明白的呀
可不可以稍微解释 通俗一点 他们的文档看的云里雾里的~~~~
xian_wwq 2019-04-18
  • 打赏
  • 举报
回复
https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentqueue-1?redirectedfrom=MSDN&view=netframework-4.7.2
微软的文档说的挺明白的呀
干拉痞痞虾 2019-04-18
  • 打赏
  • 举报
回复
引用 3 楼 stherix 的回复:
你用普通的队列比如Queue<> 多线程同时操作的话,可能会造成数据错误,比如不同线程push,pop多个数据,会造成大小和数据紊乱 因为访问head坐标和访问值不是原子操作 比如if(list.Count>0) list[0].xxxx 在多线程下,就算if判断成功,后面取list[0]也可能出错 ConcurrentQueue就是解决了这种类似的问题
在以往的使用Queue<T> 中,若想在多线程的环境下,使用,必须的自己定义个Lock锁, 这个ConcurrentQueue 是不是就不用定义锁,使用的时候直接就是入队 出队即可???那些所谓的资源竞争的问题,,ConcurrentQueue里面已经设置好这种机制了吗

110,570

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧