AutoResetEvent的异步(等待)等价物是什么?
如果在经典线程同步中我们会使用这样的东西:
AutoResetEvent signal = new AutoResetEvent(false);
void Thread1Proc()
{
//do some stuff
//..
//..
signal.WaitOne(); //wait for an outer thread to signal we are good to continue
//do some more stuff
//..
//..
}
void Thread2Proc()
{
//do some stuff
//..
//..
signal.Set(); //signal the other thread it's good to go
//do some more stuff
//..
//..
}
我希望以新的异步方式做事,这样的事情会变成:
SomeAsyncAutoResetEvent asyncSignal = new SomeAsyncAutoResetEvent();
async void Task1Proc()
{
//do some stuff
//..
//..
await asyncSignal.WaitOne(); //wait for an outer thread to signal we are good to continue
//do some more stuff
//..
//..
}
async void Task2Proc()
{
//do some stuff
//..
//..
asyncSignal.Set(); //signal the other thread it's good to go
//do some more stuff
//..
//..
}
我已经看过其他定制解决方案,但是我在某个时间点设法接触到的仍然涉及锁定线程。我不希望这只是为了使用新的await语法。我正在寻找一种真正的等待信号机制,它不会锁定任何线程。
这是我在任务并行库中缺少的东西吗?
编辑:只是为了说清楚:SomeAsyncAutoResetEvent是一个完全组成的类名,在我的例子中用作占位符。
答案 0 :(得分:19)
如果您想构建自己的Stephen Toub has the definitive blog post on the subject。
如果您想使用已编写的I have one in my AsyncEx library。 AFAIK,截至本文撰写时尚无其他选择。
答案 1 :(得分:12)
这是斯蒂芬·图布的AsyncAutoResetEvent
的{{3}},以防他的博客离线。
public class AsyncAutoResetEvent
{
private static readonly Task s_completed = Task.FromResult(true);
private readonly Queue<TaskCompletionSource<bool>> m_waits = new Queue<TaskCompletionSource<bool>>();
private bool m_signaled;
public Task WaitAsync()
{
lock (m_waits)
{
if (m_signaled)
{
m_signaled = false;
return s_completed;
}
else
{
var tcs = new TaskCompletionSource<bool>();
m_waits.Enqueue(tcs);
return tcs.Task;
}
}
}
public void Set()
{
TaskCompletionSource<bool> toRelease = null;
lock (m_waits)
{
if (m_waits.Count > 0)
toRelease = m_waits.Dequeue();
else if (!m_signaled)
m_signaled = true;
}
toRelease?.SetResult(true);
}
}
答案 2 :(得分:7)
我认为MSDN上有很好的例子:https://msdn.microsoft.com/en-us/library/hh873178%28v=vs.110%29.aspx#WHToTap
public static Task WaitOneAsync(this WaitHandle waitHandle)
{
if (waitHandle == null)
throw new ArgumentNullException("waitHandle");
var tcs = new TaskCompletionSource<bool>();
var rwh = ThreadPool.RegisterWaitForSingleObject(waitHandle,
delegate { tcs.TrySetResult(true); }, null, -1, true);
var t = tcs.Task;
t.ContinueWith( (antecedent) => rwh.Unregister(null));
return t;
}
答案 3 :(得分:3)
这是我制作的版本,允许您指定超时。它来自Stephen Toub的解决方案。我们目前在生产工作负载中使用它。
public class AsyncAutoResetEvent
{
readonly LinkedList<TaskCompletionSource<bool>> waiters =
new LinkedList<TaskCompletionSource<bool>>();
bool isSignaled;
public AsyncAutoResetEvent(bool signaled)
{
this.isSignaled = signaled;
}
public Task<bool> WaitAsync(TimeSpan timeout)
{
return this.WaitAsync(timeout, CancellationToken.None);
}
public async Task<bool> WaitAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
TaskCompletionSource<bool> tcs;
lock (this.waiters)
{
if (this.isSignaled)
{
this.isSignaled = false;
return true;
}
else if (timeout == TimeSpan.Zero)
{
return this.isSignaled;
}
else
{
tcs = new TaskCompletionSource<bool>();
this.waiters.AddLast(tcs);
}
}
Task winner = await Task.WhenAny(tcs.Task, Task.Delay(timeout, cancellationToken));
if (winner == tcs.Task)
{
// The task was signaled.
return true;
}
else
{
// We timed-out; remove our reference to the task.
// This is an O(n) operation since waiters is a LinkedList<T>.
lock (this.waiters)
{
bool removed = this.waiters.Remove(tcs);
Debug.Assert(removed);
return false;
}
}
}
public void Set()
{
lock (this.waiters)
{
if (this.waiters.Count > 0)
{
// Signal the first task in the waiters list. This must be done on a new
// thread to avoid stack-dives and situations where we try to complete the
// same result multiple times.
TaskCompletionSource<bool> tcs = this.waiters.First.Value;
Task.Run(() => tcs.SetResult(true));
this.waiters.RemoveFirst();
}
else if (!this.isSignaled)
{
// No tasks are pending
this.isSignaled = true;
}
}
}
public override string ToString()
{
return $"Signaled: {this.isSignaled.ToString()}, Waiters: {this.waiters.Count.ToString()}";
}
}
答案 4 :(得分:0)
我扩展了 Oleg Gordeev 提供的 MSDN 示例,并带有可选的超时(毫秒):
public static Task WaitOneAsync(this WaitHandle waitHandle, double timeout = 0)
{
if (waitHandle == null) throw new ArgumentNullException("waitHandle");
var tcs = new TaskCompletionSource<bool>();
if (timeout > 0)
{
var timer = new System.Timers.Timer(timeout)
{ Enabled = true, AutoReset = false };
ElapsedEventHandler del = default;
del = delegate (object x, System.Timers.ElapsedEventArgs y)
{
tcs.TrySetResult(true);
timer.Elapsed -= del;
timer.Dispose();
};
timer.Elapsed += del;
}
var rwh = ThreadPool.RegisterWaitForSingleObject(waitHandle,
delegate { tcs.TrySetResult(true); },
null, -1, true);
var t = tcs.Task;
t.ContinueWith((antecedent) => rwh.Unregister(null));
return t;
}
答案 5 :(得分:-1)
这是我的一次性事件版本,可以由多个线程等待。它在内部依赖于 BoundedChannel
。
public class AsyncOneTimeEvent<T>
{
private T Result { get; set; }
private readonly Channel<bool> _channel = Channel.CreateBounded<bool>(new BoundedChannelOptions(1)
{
SingleReader = false,
SingleWriter = true,
FullMode = BoundedChannelFullMode.DropWrite,
});
public async Task<T> GetResult()
{
await _channel.Reader.WaitToReadAsync().ConfigureAwait(false);
return this.Result;
}
public void SetResult(T result)
{
this.Result = result;
_channel.Writer.Complete();
}
public void SetError(Exception ex)
{
_channel.Writer.Complete(ex);
}
}
答案 6 :(得分:-2)
它也可以使用,但是这种方式可能会削弱使用eval_config: {
metrics_set: "coco_detection_metrics"
num_examples: 1000
# Note: The below line limits the evaluation process to 10 evaluations.
# Remove the below line to evaluate indefinitely.
max_evals: 10
}
eval_input_reader: {
tf_record_input_reader {
input_path: "path_to_test*.record"
}
label_map_path: "path_to_labelmap.pbtxt that you use to train"
shuffle: false
num_readers: 1
}
和java.net.Inet6AddressImpl.lookupAllHostAddr
的目的。
java.net.preferIPv4Stack=true