【发布时间】:2017-10-18 15:10:15
【问题描述】:
我有两个线程的简单场景,其中第一个线程永久读取一些数据并将该数据排入队列。第二个线程首先从该队列中查看单个对象并进行一些条件检查。如果这些都很好,则单个对象将出列并传递给某些处理。
我尝试使用ConcurrentQueue,它是一个简单队列的线程安全实现,但是这个问题是所有调用都被阻塞了。这意味着如果第一个线程将对象入队,则第二个线程无法查看或出队对象。
在我的情况下,我需要同时从队列的开头入队和出队。
C#的lock语句也会。
所以我的问题是是否可以并行执行这两个操作而不会以线程安全的方式相互阻塞。
这是我的第一次尝试,这是我的问题的类似示例。
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Scenario {
public class Program {
public static void Main(string[] args) {
Scenario scenario = new Scenario();
scenario.Start();
Console.ReadKey();
}
public class Scenario {
public Scenario() {
someData = new Queue<int>();
}
public void Start() {
Task.Factory.StartNew(firstThread);
Task.Factory.StartNew(secondThread);
}
private void firstThread() {
Random random = new Random();
while (true) {
int newData = random.Next(1, 100);
someData.Enqueue(newData);
Console.WriteLine("Enqueued " + newData);
}
}
private void secondThread() {
Random random = new Random();
while (true) {
if (someData.Count == 0) {
continue;
}
int singleData = someData.Peek();
int someValue = random.Next(1, 100);
if (singleData > someValue || singleData == 1 || singleData == 99) {
singleData = someData.Dequeue();
Console.WriteLine("Dequeued " + singleData);
// ... processing ...
}
}
}
private readonly Queue<int> someData;
}
}
}
第二个例子:
public class Scenario {
public Scenario() {
someData = new ConcurrentQueue<int>();
}
public void Start() {
Task.Factory.StartNew(firstThread);
Task.Factory.StartNew(secondThread);
}
private void firstThread() {
Random random = new Random();
while (true) {
int newData = random.Next(1, 100);
someData.Enqueue(newData);
lock (syncRoot) { Console.WriteLine($"Enqued {enqued++} Dequed {dequed}"); }
}
}
private void secondThread() {
Random random = new Random();
while (true) {
if (!someData.TryPeek(out int singleData)) {
continue;
}
int someValue = random.Next(1, 100);
if (singleData > someValue || singleData == 1 || singleData == 99) {
if (!someData.TryDequeue(out singleData)) {
continue;
}
lock (syncRoot) { Console.WriteLine($"Enqued {enqued} Dequed {dequed++}"); }
// ... processing ...
}
}
}
private int enqued = 0;
private int dequed = 0;
private readonly ConcurrentQueue<int> someData;
private static readonly object syncRoot = new object();
}
【问题讨论】:
-
您可以使用第三个线程来处理队列的所有工作,另外两个线程与第三个线程通信,它们不直接使用队列
-
@EhsanZargarErshadi 除了为一个没有任何生产力的额外线程消耗更多系统资源之外,这并没有为您带来任何好处。
-
为什么同时添加和删除对您很重要?通常,产生或使用该值所需的处理比实际从队列中添加或删除项目所需的时间显着昂贵,因此锁争用往往较低。您在实践中实际上遇到了哪些问题,导致无法接受简单地同步访问队列?
-
@Servy 我不同意。我已经用并发队列和一些计数器重写了我的示例,并且 dequeing 在某个数字处停止,例如 260 次。在那之后,只有 enquing 正在发生。已编辑的帖子。
-
@StudentBanana 您还同步了整个处理过程。这意味着在两者之间实际上没有任何工作可以并行完成。如果您实际上并行处理项目,则同步访问队列不会有问题。
标签: c# .net queue thread-safety nonblocking