我现在的情况是,我想确保写入是以编程方式进行的。我能想到的唯一方法是将注销发送到库,并等待读取来更新写入效果的数据。
是的,这可能是唯一的方法,但有比旋转等待读取更新是否发生更好的方法。
下面是一个处理过程的示例方法,就像我们在
the chat
我们做到了。总之,您有一个队列
IDataRequest
,在这些请求中,他们持有
TaskCompletionSource<T>
表示数据发送完成。一旦您向设备发出请求并得到响应,就可以设置完成源的结果。我将它与您现有的基于事件的实现结合起来,但老实说,我会删除事件的,只是让调用者在等待结果后更新UI
RequestTemp()
.
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);
}
}