C# 阻止并发收集,如ConcurrentBag或BlockingCollection

C# 阻止并发收集,如ConcurrentBag或BlockingCollection,c#,.net,multithreading,C#,.net,Multithreading,我想执行两条语句访问一个ConcurrentBag,其间没有任何其他线程访问它。例如: ConcurrentBag<int> concurrentBag = new ConcurrentBag<int>(); concurrentBag.Add(1); int check = 0; //Somehow block so that the following two statements will //be executed without any other threa

我想执行两条语句访问一个
ConcurrentBag
,其间没有任何其他线程访问它。例如:

ConcurrentBag<int> concurrentBag = new ConcurrentBag<int>();
concurrentBag.Add(1);
int check = 0;
//Somehow block so that the following two statements will 
//be executed without any other thread accessing concurrentBag.
concurrentBag.TryPeek(out check);
if (check == 2) concurrentBag.Add(3);
ConcurrentBag ConcurrentBag=新的ConcurrentBag();
添加(1);
整数检查=0;
//以某种方式阻止,以便以下两个语句
//在没有任何其他线程访问concurrentBag的情况下执行。
concurrentBag.TryPeek(结帐);
如果(检查==2)concurrentBag.Add(3);
如何做到这一点?

静态对象同步=新对象();
static object syncLock = new object();
ConcurrentBag<int> concurrentBag = new ConcurrentBag<int>();
concurrentBag.Add(1);
int check = 0;
lock(syncLock)
{
    concurrentBag.TryPeek(out check);
    if (check == 2) concurrentBag.Add(3);
}
ConcurrentBag ConcurrentBag=新的ConcurrentBag(); 添加(1); 整数检查=0; 锁定(同步锁定) { concurrentBag.TryPeek(结帐); 如果(检查==2)concurrentBag.Add(3); }
静态对象同步锁=新对象();
ConcurrentBag ConcurrentBag=新的ConcurrentBag();
添加(1);
整数检查=0;
锁定(同步锁定)
{
concurrentBag.TryPeek(结帐);
如果(检查==2)concurrentBag.Add(3);
}

如果您的意思是希望无条件地阻止任何其他线程访问某个对象,那么您就不能这样做。也就是说,如果您有一些对象:

public Object foo = new Object();
在线程中,您无法阻止其他线程访问
foo
。正如您所指出的,锁不起作用,因为锁是一种协作互斥设备。除了约定之外,没有任何东西可以阻止某人编写忽略锁并访问对象的代码

可以想象,您可以将对象包装在一个强制互斥的类中,但接下来您所做的就是添加一个锁或类似的东西。即使这样,您也必须创建一个特殊的方法来实现您正在寻找的语义(即peek和add)。即使这样也不是万无一失的,因为有人可以使用反射直接访问底层数据结构


您的“查看并添加”操作有点不寻常。我对您的应用程序了解不多,不能肯定,但在大多数情况下,有一种更传统的方法来实现您想要的功能。这在类似于
ConcurrentBag
的情况下尤其如此,其中
Peek
将根据访问它的线程给出不同的结果。

如果您的意思是希望无条件阻止任何其他线程访问某个对象,那么您就不能这样做。也就是说,如果您有一些对象:

public Object foo = new Object();
在线程中,您无法阻止其他线程访问
foo
。正如您所指出的,锁不起作用,因为锁是一种协作互斥设备。除了约定之外,没有任何东西可以阻止某人编写忽略锁并访问对象的代码

可以想象,您可以将对象包装在一个强制互斥的类中,但接下来您所做的就是添加一个锁或类似的东西。即使这样,您也必须创建一个特殊的方法来实现您正在寻找的语义(即peek和add)。即使这样也不是万无一失的,因为有人可以使用反射直接访问底层数据结构


您的“查看并添加”操作有点不寻常。我对您的应用程序了解不多,不能肯定,但在大多数情况下,有一种更传统的方法来实现您想要的功能。对于像
ConcurrentBag
这样的东西尤其如此,其中
Peek
将根据访问它的线程给出不同的结果。

我怀疑这将比仅仅用
lock
关键字包装几行代码要困难得多。原因是,您必须用锁将所有访问
ConcurrentBag
的权限包装起来。如果你这样做了,那么它就不再是“并发的”。我认为您需要的是一种方法,暂时挂起集合的并发行为,以支持序列化行为,然后在完成特殊保护操作后再次恢复其并发行为

天真的方法是创建您自己的集合,通过内部使用它作为基础集合,提供与
ConcurrentBag
相同的操作。然后,您将提供
SuspendConcurrent
resumecocurrent
操作,通过设置和重置标志(可能通过
interlocted.CompareExchange
方法)来切换集合的并发行为。然后,代码将使用此标志确定是直接将
添加
TryPeek
TryTake
操作转发到基础集合,还是在执行此操作之前使用硬锁。我想最终你会发现代码非常复杂,而且很难找到正确的代码。根据个人经验,您现在设想的任何琐碎的实现都可能是错误的、低效的,或者在您希望的时候不是完全并发的

我的建议是找出一种方法来重新构造代码,从而消除这种需求

编辑:


从您的一条评论来看,似乎您希望某个操作能够根据是否已经发生了某些事情来做出决定。有几个选项,但最适合您的一个选项是使用提供
TryAdd
方法的
ConcurrentDictionary
类<如果密钥尚不存在,code>TryAdd将返回true。如果密钥确实存在,那么它将返回false。许多线程可以使用同一个键同时执行此方法,但只有一个线程会得到真正的响应。然后,您可以使用
if
语句根据返回值控制程序流。

我怀疑这将比仅用
lock
关键字包装几行代码要困难得多。原因是您必须包装all
Concur的访问