我有一个异步发现功能,可以发现本地网络中的设备,单击按钮即可调用该设备。 我正在向所有设备发送广播消息,并使用CancellationTokenSource监听响应5秒钟。令牌过期后,我将返回一个IEnummerable的解析响应给我的WPF模型。
我想直接返回传入的响应(并在5秒钟后停止收听),以便我可以在UI中立即显示已切换的设备,而不是在5秒钟后全部显示。
这是我的代码:
public async Task<IEnumerable<IDevice>> Discover()
{
var client = new MyClient();
var responseData = await GetResponseData(client);
return this.ParseResponseData(responseData);
}
private IEnumerable<IDevice> ParseResponseData(List<DeviceResponseData> responseData)
{
foreach (var data in responseData)
{
yield return DeviceFactory.Create(data);
}
}
private static async Task<List<DeviceResponseData>> GetResponseData(MyClient client, int timeout = 5000)
{
var cancellationToken = new CancellationTokenSource(timeout);
var data = new List<DeviceResponseData>();
// ... prepare message and send it
await client.SendAsync(message, new CancellationToken());
try
{
while (!cancellationToken.IsCancellationRequested)
{
// Wait indefinitely until any message is received.
var response = await client.ReceiveAsync(cancellationToken.Token);
data.Add(new DeviceResponseData(/* ... */ response));
}
}
catch (TaskCanceledException e)
{
}
return data;
}
答案 0 :(得分:0)
还不清楚您要问的是什么...但是,如果您想显示输入的结果,可以通过多种方法来实现,例如去耦消息,事件,等等。
但是,您可以只使用一个简单的Action
private static async Task<List<DeviceResponseData>> GetResponseData(MyClient client, Action<DeviceResponseData> update, int timeout = 5000)
{
var cancellationToken = new CancellationTokenSource(timeout);
...
while (!cancellationToken.IsCancellationRequested)
{
// Wait indefinitely until any message is received.
var response = await client.ReceiveAsync(cancellationToken.Token);
var result = new DeviceResponseData( /* ... */ response);
data.Add(result);
update(result);
}
...
}
用法
var allResults = await GetResponseData(client,data => UdpateUI(data), timeout);
注意 :因为这是异步等待模式,所以您不必将Action
的结果编组回UI上下文,如果这是从
答案 1 :(得分:0)
如果您已升级到C#8,则可以选择返回IAsyncEnumerable
。这种机制可以传播DeviceResponseData
对象的流,这些对象一产生就可以被消耗。这是生产者方法,实现为iterator(包含yield
条语句)。
private static async IAsyncEnumerable<DeviceResponseData> GetResponseDataStream(
MyClient client, int timeout = 5000)
{
using CancellationTokenSource cts = new CancellationTokenSource(timeout);
// Prepare message...
try
{
await client.SendAsync(message, cts.Token).ConfigureAwait(false);
}
catch (OperationCanceledException) { throw new TimeoutException(); }
while (!cts.IsCancellationRequested)
{
Response response;
try
{
// Wait until any response is received.
response = await client.ReceiveAsync(cts.Token).ConfigureAwait(false);
}
catch (OperationCanceledException) { yield break; }
yield return new DeviceResponseData(/* ... */ response);
}
}
这是如何食用它:
await foreach (DeviceResponseData data in GetResponseDataStream(client))
{
// Do something with data
}
最好的部分是,您不必在每次收到Dispatcher.Invoke
对象时都添加同步代码(DeviceResponseData
等)。异步/等待机制会自动为您恢复捕获的同步上下文(除非您通过使用ConfigureAwait
方法告诉它执行其他操作)。
重要:请确保您dispose个CancellationTokenSource
创建的对象。