我得到了以下代码(在多线程环境中效果不佳)
public class SomeClass
{
private readonly ConcurrentQueue<ISocketWriterJob> _writeQueue = new ConcurrentQueue<ISocketWriterJob>();
private ISocketWriterJob _currentJob;
public void Send(ISocketWriterJob job)
{
if (_currentJob != null)
{
_writeQueue.Enqueue(job);
return;
}
_currentJob = job;
_currentJob.Write(_writeArgs);
// The job is invoked asynchronously here
}
private void HandleWriteCompleted(SocketError error, int bytesTransferred)
{
// error checks etc removed for this sample.
if (_currentJob.WriteCompleted(bytesTransferred))
{
_currentJob.Dispose();
if (!_writeQueue.TryDequeue(out _currentJob))
{
_currentJob = null;
return;
}
}
_currentJob.Write(_writeArgs);
// the job is invoked asycnhronously here.
}
}
如果没有正在执行的当前作业,Send方法应异步调用作业。如果存在,它应该排队。
锁定_currentJob
任务/检查会使一切正常。但是有解锁方法吗?
更新
我正在使用套接字,它是SendAsync
方法来发送信息。这意味着在调用Send()
方法时,我不知道是否有写入/作业挂起。
答案 0 :(得分:4)
考虑使用CompareExchange
关于预期状态转换的假设。不需要使用ConcurrentQueue,因为现在我们可以控制同步。
更新了以使用状态机
再次更新以删除不需要的Interlocked.Exchange
(用于状态分配)。
public class SomeClass
{
private readonly Queue<ISocketWriterJob> _writeQueue = new Queue<ISocketWriterJob>();
private ISocketWriterJob _currentJob;
private enum State { Idle, Active, Enqueue, Dequeue };
private State _state;
public void Send(ISocketWriterJob job)
{
bool spin = true;
while(spin)
{
switch(_state)
{
case State.Idle:
if (Interlocked.CompareExchange(ref _state, State.Active, State.Idle) == State.Idle)
{
spin = false;
}
// else consider new state
break;
case State.Active:
if (Interlocked.CompareExchange(ref _state, State.Enqueue, State.Active) == State.Active)
{
_writeQueue.Enqueue(job);
_state = State.Active;
return;
}
// else consider new state
break;
case State.Enqueue:
case State.Dequeue:
// spin to wait for new state
Thread.Yield();
break;
}
}
_currentJob = job;
_currentJob.Write(_writeArgs);
// The job is invoked asynchronously here
}
private void HandleWriteCompleted(SocketError error, int bytesTransferred)
{
// error checks etc removed for this sample.
if (_currentJob.WriteCompleted(bytesTransferred))
{
_currentJob.Dispose();
bool spin = true;
while(spin)
{
switch(_state)
{
case State.Active:
if (Interlocked.CompareExchange(ref _state, State.Dequeue, State.Active) == State.Active)
{
if (!_writeQueue.TryDequeue(out _currentJob))
{
// handle in state _currentJob = null;
_state = State.Idle;
return;
}
else
{
_state = State.Active;
}
}
// else consider new state
break;
case State.Enqueue:
// spin to wait for new state
Thread.Yield();
break;
// impossible states
case State.Idle:
case State.Dequeue:
break;
}
}
}
_logger.Debug(_writeArgs.GetHashCode() + ": writing more ");
_currentJob.Write(_writeArgs);
// the job is invoked asycnhronously here.
}
}
答案 1 :(得分:1)
目前,您的生产者和消费者之间的分歧有点模糊;你有“将一份工作产生到一个队列或立即消费”和“从队列中消耗一份工作或者如果没有一份就退出”;它会更清晰,因为“在队列中生成一个作业”和“从队列中消耗一个作业(最初)”和“从队列中消耗一个作业(一旦作业完成”)。
这里的诀窍是使用BlockingCollection
,这样您就可以等待来显示作业:
BlockingCollection<ISocketWriterJob> _writeQueue =
new BlockingCollection<ISocketWriterJob>();
让调用Send
的线程只是将作业排队:
public void Send(ISocketWriterJob job)
{
_writeQueue.Add(job);
}
然后让另一个线程只消耗工作。
public void StartConsumingJobs()
{
// Get the first job or wait for one to be queued.
_currentJob = _writeQueue.Take();
// Start job
}
private void HandleWriteCompleted(SocketError error, int bytesTransferred)
{
if (_currentJob.WriteCompleted(bytesTransferred))
{
_currentJob.Dispose();
// Get next job, or wait for one to be queued.
_currentJob = _writeQueue.Take();
}
_currentJob.Write(_writeArgs);
// Start/continue job as before
}
答案 2 :(得分:0)
我不认为你会从使用无锁技术中获得一些东西。即使使用简单锁定,您也可以保持用户模式,因为Monitor.Enter
/ Monitor.Exit
首先使用了旋转,只有当您在等待状态下等待更长时间时才会转换进入内核模式。
这意味着基于锁的技术将表现得与任何无锁技术一样好,因为您只能锁定将作业存储到队列中并从中取回它,但是您将拥有更加清晰和强大的功能每个开发人员都能理解的代码:
public class SomeClass
{
// We don't have to use Concurrent collections
private readonly Queue<ISocketWriterJob> _writeQueue = new Queue<ISocketWriterJob>();
private readonly object _syncRoot = new object();
private ISocketWriterJob _currentJob;
public void Send(ISocketWriterJob job)
{
lock(_syncRoot)
{
if (_currentJob != null)
{
_writeQueue.Enqueue(job);
return;
}
_currentJob = job;
}
// Use job instead of shared state
StartJob(job);
}
private void StartJob(ISocketWriterJob job)
{
job.Write(_writeArgs);
// The job is invoked asynchronously here
}
private void HandleWriteCompleted(SocketError error, int bytesTransferred)
{
ISocketWriterJob currentJob = null;
// error checks etc removed for this sample.
lock(_syncRoot)
{
// I suppose this operation pretty fast as well as Dispose
if (_currentJob.WriteCompleted(bytesTransferred))
{
_currentJob.Dispose();
// There is no TryDequeue method in Queue<T>
// But we can easily add it using extension method
if (!_writeQueue.TryDequeue(out _currentJob))
{
// We don't have set _currentJob to null
// because we'll achieve it via out parameter
// _currentJob = null;
return;
}
}
// Storing current job for further work
currentJob = _currentJob;
}
StartJob(currentJob);
}
}
无锁是一种优化,就像任何其他优化一样,您应首先测量性能,以确保您的简单基于锁的实现存在问题,并且只有在其真实时才使用 - 使用一些较低级别的技术,如无锁。性能和可维护性是一种经典的权衡,您应该非常谨慎地选择。
答案 3 :(得分:-2)
您可以将当前作业标记为volatile
,以确保当前线程获得最新状态。一般来说,锁定是有利的。
private volatile ISocketWriterJob _currentJob;