我有一个控制台应用程序,它有一些功能,我有一个类做一些操作,比如将一些消息发布到服务。现在在每个消息上发布该类我正在递增一个计数器。我有一个与该计数器关联的委托,因此在达到限制后,我在Program.cs上有一个事件来阻止该调用。现在我正在做发布部分异步。那么如何保护专柜财产。
以下是我的代码:
var objMessageManager = new MessageManager();
objMessageManager.MaximumLimitToStopRequest += objMessageManager_MaximumLimitToStopRequest;
static void objMessageManager_MaximumLimitToStopRequest(object sender, int ItemCount)
{
if (ItemCount > Settings.MessageLimit)
{
Task.Delay(Settings.MessageDelayTime);
}
}
internal delegate void StopRequestEventHandler(object sender, int ProcessedItem);
public event StopRequestEventHandler MaximumLimitToStopRequest;
private int ProcessedItem;
private int noOfProcessed;
public int NoOfProcessed
{
get
{
return ProcessedItem;
}
private set
{
ProcessedItem = value;
if (MaximumLimitToStopRequest != null)
{
MaximumLimitToStopRequest(this, ProcessedItem);
}
}
}
internal async void CreateMessage(xyz sMessageObj)
{
try
{
await Task.Run(() =>
{
// Code to publish the message
});
Interlocked.Increment(ref noOfProcessed);
NoOfProcessed = noOfProcessed;
}
catch (Exception ex)
{
Log.Error("Exception occurred .", ex);
}
}
请忽略变量和所有的命名错误。应用程序运行正常。我需要帮助才能使读取和写入/递增 NoOfProcessed 属性的线程安全。
答案 0 :(得分:2)
不,您的代码在任何情况下都不是线程安全的。首先,在StopRequestEventHandler
课程中,您没有将ProcessedItem
或noOfProcessed
标记为volatile
。其次,Interlocked.Increment
是不够的,您必须使用Interlocked
,CompareExchange
的CAS操作。第三,您应该考虑创建queue
,因为ProcessedItem
属性可以是race-condition
的目标。
最简单形式的queue
只是Array
更新后的值,因此我们有一个数组,并且currentPosition
就可以了。在CAS-Exchange
operation之后,您只需基于索引独立于其他线程使用数组项。所以代码就像这样(这是一个非常简单的实现,你应该尝试编写自己的代码):
int[] processedItems = new int[INITIAL_SIZE];
int currentItem = 0;
var current = currentItem;
// make first attempt...
if (Interlocked.CompareExchange(ref currentItem, current + 1, current) != current)
{
// if we fail, go into a spin wait, spin, and try again until succeed
var spinner = new SpinWait();
do
{
spinner.SpinOnce();
current = currentItem;
}
while (Interlocked.CompareExchange(ref currentItem, current + 1, current) != current);
}
// store the value in array
processedItems[current] = value
答案 1 :(得分:0)
说实话,我对你的代码很困惑。
你有
private int ProcessedItem;
private int noOfProcessed;
然后你的财产(也有奇怪的名字)是
public int NoOfProcessed
{
get
{
return ProcessedItem;
}
private set
{
ProcessedItem = value;
if (MaximumLimitToStopRequest != null)
{
MaximumLimitToStopRequest(this, ProcessedItem);
}
}
}
但是您在逻辑中增加了noOfProcessed
Interlocked.Increment(ref noOfProcessed);
我建议像这样重写你的代码:
private int noOfProcessed;
...
...
Interlocked.Increment(ref noOfProcessed);
if (MaximumLimitToStopRequest != null) // Warning: this is NOT thread safe
{
MaximumLimitToStopRequest(this, noOfProcessed);
}
...
public int NoOfProcessed
{
get
{
return noOfProcessed;
}
}
或者像这样(由于你以线程安全的方式包装事件调用,它会更慢但更安全)
lock(_syncLock)
{
++noOfProcessed;
if (MaximumLimitToStopRequest != null)
{
MaximumLimitToStopRequest(this, noOfProcessed);
}
}
我非常喜欢VMAtm的排队想法。线程安全队列似乎更适合您的任务,但上述解决方案(特别是lock
)应该是线程安全的,只要您在单个应用程序域中操作,并且这是唯一的位置调用事件。
答案 2 :(得分:-1)
您可以使用ReaderWriterLockSlim。
这是一个锁,它可以让你保持线程安全并保持一点性能。
使用
try
{
// lock you code in write to change the value and read to just read it.
}
finally
{
// release the lock
}
锁定(ReaderWriterLockSlim)只有在询问写入锁定时才允许读者通过并锁定。