我的应用程序中有一个模式,我想发送一些命令或启动一些IO工作并等待它完成,或者有一些机制来知道它是否已经完成。
为此,我计划使用async
/ await
模式。我假设大多数人已经到了编码的地步,他们达到了这种模式。
while(something)
DoNothing();
DoNothing()
通常会占用一些CPU时间或完全停止程序。我认为解决这个问题的正确方法是使用async
/ await
模式。
就我而言,我提出了以下简单方法
public override async Task<Boolean> PerformProcessingAsync()
{
StartSomeIOProcessing();
while (TheIOProcessingResult == null)
await Task.Yield();
return true;
}
启动某些IO处理,然后等待实例化结果。同时,虽然它调用Task.Yield
返回到调用上下文,然后可以继续工作,并将此方法的延续(下一个while循环迭代)放到调用堆栈上。
这是正确的解释,这是解决上述情况的正确方法吗?
编辑:在我面临的更具体的情况下......我还维护另一个执行IO工作的库,基本上是对SerialPort
个对象的读写。该库通过对特定端口进行处理的ConcurrentQueue
读取或写入来工作。 Task
“位于此队列的末尾,并随着工作消耗。
大多数阅读工作只是查询某些值,解析它,并触发由UI监听的事件NewData(double myNewData)
。在UI中,保存了SerialPort
数据的状态表示。在状态表示中,处理NewData
事件,该事件更新它对应的任何值。
写入以相同的方式完成,但是在完成时没有触发事件,只是写入端口。判断它是否成功的唯一方法是等待读取更新状态。 (遗憾的是,由于硬件的作用,没有更好的方法可以做到这一点。)
我当前的应用程序使用此库来执行其IO工作。读取会定期发送到库以保持UI具有来自端口的新值...当用户单击按钮时,写入将被发送到库以从硬件命令。
我处于这样的情况,我想确保写入已经发生以编程方式。我能想到的唯一方法是将写入发送到库中,并等待读取更新数据写入效果。
因此循环。
答案 0 :(得分:1)
我处于这样的情况,我想确保写入已经以编程方式发生。我能想到的唯一方法是将写入发送到库中,并等待读取更新数据写入效果。
是的,这可能是唯一的方法,但有更好的方法,而不是旋转等待查看读取更新是否发生。
这是一个处理处理的示例方法,就像我们在the chat中所说的那样。总而言之,您的队列为IDataRequest
,在这些请求中,他们持有TaskCompletionSource<T>
代表发送数据的完成情况。一旦您向设备发出请求并获得响应,您就可以设置完成源的结果。我将它与您现有的基于事件的implmentation结合起来,但老实说,我会放弃事件,并等待调用者在等待RequestTemp()
的结果后对UI进行更新。
public interface IDataRequest
{
bool TrySetException(Exception ex);
}
public abstract class DataRequest<T> : IDataRequest
{
public TaskCompletionSource<T> RequestTask { get; } = new TaskCompletionSource<T>();
public bool TrySetException(Exception ex)
{
return RequestTask.TrySetException(ex);
}
}
public class TempRequest : DataRequest<double>
{
}
public class RpmRequest : DataRequest<int>
{
}
public sealed class DeviceManager : IDisposable
{
private readonly Task _workerThread;
private readonly BlockingCollection<IDataRequest> _queue;
private readonly SerialPort _serialPort;
public DeviceManager()
{
_queue = new BlockingCollection<IDataRequest>();
_workerThread = Task.Factory.StartNew(ProcessQueue, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
_serialPort = //...
}
public event EventHandler<double> TempUpdate;
public event EventHandler<int> RpmUpdate;
public Task<double> RequestTemp()
{
var request = new TempRequest();
_queue.Add(request);
return request.RequestTask.Task;
}
public Task<int> RequestRpm()
{
var request = new RpmRequest();
_queue.Add(request);
return request.RequestTask.Task;
}
public void Dispose()
{
_queue.CompleteAdding();
_workerThread.Wait();
}
private void ProcessQueue()
{
foreach (var dataRequest in _queue.GetConsumingEnumerable())
{
try
{
if (dataRequest is TempRequest)
{
DoTempRequest((TempRequest)dataRequest);
}
else if (dataRequest is RpmRequest)
{
DoRpmRequest((RpmRequest)dataRequest);
}
else
{
throw new NotSupportedException($"A Request of type {dataRequest.GetType()} is not supported.");
}
}
catch (Exception ex)
{
dataRequest.TrySetException(ex);
}
}
}
private void DoTempRequest(TempRequest dataRequest)
{
_serialPort.WriteLine("Temp ?");
var line = _serialPort.ReadLine();
double result;
//I am deliberately using Parse instead of TryParse so responses that
//fail to parse will throw and get their exception propagated up via the
//catch in ProcessQueue().
result = double.Parse(line);
//Sends the info back to the caller saying it is done and what the result was.
dataRequest.RequestTask.TrySetResult(result);
//Raises the event so subscribers know the new value.
OnTempUpdate(result);
}
private void DoRpmRequest(RpmRequest dataRequest)
{
_serialPort.WriteLine("RPM ?");
var line = _serialPort.ReadLine();
int result;
result = int.Parse(line);
dataRequest.RequestTask.TrySetResult(result);
OnRpmUpdate(result);
}
private void OnTempUpdate(double result)
{
TempUpdate?.Invoke(this, result);
}
private void OnRpmUpdate(int result)
{
RpmUpdate?.Invoke(this, result);
}
}
答案 1 :(得分:0)
我会将StartSomethingIOProcessing
提取到一个返回结果的单独Task
中。 Await
就此而言,然后检查结果。
public async PerformProcessingAsync()
{
var ioProcessingResult = await StartSomeIOProcessing();
return (null != ioProcessingResult);
}
private Task<TheIOProcessingResultType> StartSomeIOProcessing()
{
return Task.Run(()=>
{
return StartSomeIOProcessing();
});
}
答案 2 :(得分:-1)
我相信你误解了async / await操作。 await关键字实际上用于等待异步操作完成,因此您不再需要实现while循环并检查它是否为空。
public override async Task<Boolean> PerformProcessingAsync()
{
await Task.Run((() =>
{
StartSomeIOProcessing();
});
return true;
}