问题
在项目案例中,我需要创建多个线程,这些线程从队列中挑选任务并运行它们。如果一组其他任务仍在运行,则其中一些任务无法运行。在Windows中考虑文件复制和碎片整理(在系统空闲时运行)等内容。
解决方案
为了实现这一点,我创建了一个基于的类 System.Threading.CountdownEvent
每当线程从队列中选择阻止任务时,他们将Increment
CounterEvent
,并且在他们完成工作后,他们将Decrement
CounterEvent
。
如果一个线程选择一个低优先级的任务,它将Wait
直到CounterEvent
为零然后开始运行。
低优先级广告可以立即从Reset
CounterEvent
开头
主线程或并行线程可以通过查询CurrentCount
来监控锁定状态。
以下是代码:
using System;
using System.Diagnostics.Contracts;
using System.Threading;
public class CounterEvent : IDisposable {
private volatile int m_currentCount;
private volatile bool m_disposed;
private ManualResetEventSlim m_event;
// Gets the number of remaining signals required to set the event.
public int CurrentCount {
get {
return m_currentCount;
}
}
// Allocate a thin event, Create a latch in signaled state.
public CounterEvent() {
m_currentCount = 0;
m_event = new ManualResetEventSlim();
m_event.Set(); //
}
// Decrements the counter. if counter is zero signals other threads to continue
public void Decrement() {
ThrowIfDisposed();
Contract.Assert(m_event != null);
int newCount = 0;
if (m_currentCount >= 0) {
#pragma warning disable 0420
newCount = Interlocked.Decrement(ref m_currentCount);
#pragma warning restore 0420
}
if (newCount == 0) {
m_event.Set();
}
}
// increments the current count by one.
public void Increment() {
ThrowIfDisposed();
#pragma warning disable 0420
Interlocked.Increment(ref m_currentCount);
#pragma warning restore 0420
}
// Resets the CurrentCount to the value of InitialCount.
public void Reset() {
ThrowIfDisposed();
m_currentCount = 0;
m_event.Set();
}
// Blocks the current thread until the System.Threading.CounterEvent is set.
public void Wait() {
ThrowIfDisposed();
m_event.Wait();
}
/// <summary>
/// Throws an exception if the latch has been disposed.
/// </summary>
private void ThrowIfDisposed() {
if (m_disposed) {
throw new ObjectDisposedException("CounterEvent");
}
}
// According to MSDN this is not thread safe
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
// According to MSDN Dispose() is not thread-safe.
protected virtual void Dispose(bool disposing) {
if (disposing) {
m_event.Dispose();
m_disposed = true;
}
}
}
问题
这段代码会按预期工作吗? 我没有看到任何瑕疵吗? 这样做有更好的选择吗?
注意
应用程序是用System.Threading.Thread编写的,为我转换它的成本非常高,但是一个很好的替代解决方案总是值得为将来工作。
答案 0 :(得分:1)
This should be one atomic operation and it is not threadsafe if you do it like this
if (m_currentCount >= 0)
{
newCount = Interlocked.Decrement(ref m_currentCount);
}
It may happen that m_currentCount
is changed between the if
and the Interlocked.Decrement
You should rewrite your logic to use Interlocked.CompareExchange
I would also use Interlocked.Exchange
in every place where you assign to m_currentCount
then you don´t need volatile
and the pragma
You should also be aware of that under very heavy load it can happen that a reset event Set
is getting lost