C# 用于长时间运行同步方法的异步方法
大家好,我对所有异步的东西都很陌生,如果你们能给我一些建议,那就太好了。我不确定我的方法是否可行 假设我有一个总线设备正在读取数据,如果电报完成,它就会触发一个事件。现在我想检查每封电报的长度。如果长度==期望值->如果不尝试,则单击OK或超时。但它想同时检查长度1、2和5 更新: 好的,我将我的示例更改为异步方法,但我仍然不知道这将如何帮助我解决问题?好吧,好的一面是,我不再有线程在大部分时间被阻塞,但这不是我的问题: 所以我试着用另一种方式来解释。我想要一个异步方法,在总线上侦听并返回与定义长度匹配的电报C# 用于长时间运行同步方法的异步方法,c#,asynchronous,task-parallel-library,async-await,c#-5.0,C#,Asynchronous,Task Parallel Library,Async Await,C# 5.0,大家好,我对所有异步的东西都很陌生,如果你们能给我一些建议,那就太好了。我不确定我的方法是否可行 假设我有一个总线设备正在读取数据,如果电报完成,它就会触发一个事件。现在我想检查每封电报的长度。如果长度==期望值->如果不尝试,则单击OK或超时。但它想同时检查长度1、2和5 更新: 好的,我将我的示例更改为异步方法,但我仍然不知道这将如何帮助我解决问题?好吧,好的一面是,我不再有线程在大部分时间被阻塞,但这不是我的问题: 所以我试着用另一种方式来解释。我想要一个异步方法,在总线上侦听并返回与定义
async Task<byte[]> GetTelegramAsync(int length, Timespan timeout)
我想做这样的事情
Task<byte[]> t1 = GetTelegramAsync(1);
Task<byte[]> t2 = GetTelegramAsync(6);
Task<byte[]> t4 = GetTelegramAsync(4);
Task t4 = DoOtherStuffAsync();
DoStuff();
Task.WaitAll(AsyncRsp(t1), AsyncRsp(t2), AsyncRsp(t3), t4);
/* Output
Get telegram with length of 1
Get telegram with length of 6
Get telegram with length of 4
Start doing other async stuff
Sync stuff done...
Telegram found 0x00 0x01 0x02 0x03 0x04 0x05
Async stuff done...
Telegram found 0xFF
Telegram with length 4 not found
*/
这是我的第一节设备课。如果收到电报,则启动一个线程侦听总线,并触发事件
class BusDeviceThread
{
private readonly Random _r = new Random();
private Thread _t;
public event EventHandler<TelegramReceivedArgs> TelegramReceived;
public void Connect()
{
_t = new Thread(FetchBusData)
{
Name = "FetchBusData",
Priority = ThreadPriority.Normal
};
_t.Start();
}
public void Close()
{
_t.Abort();
_t.Join();
}
private void FetchBusData()
{
while (true)
{
Thread.Sleep(_r.Next(100, 1000));
var buffer = new byte[_r.Next(1, 10)];
_r.NextBytes(buffer);
OnTelegramReceived(new TelegramReceivedArgs(buffer));
}
}
private void OnTelegramReceived(TelegramReceivedArgs e)
{
var handler = TelegramReceived;
if (handler != null) handler(this, e);
}
}
下面是使用async await更改的BusDevice类
class BusDeviceAsync
{
private readonly Random _r = new Random();
public event EventHandler<TelegramReceivedArgs> TelegramReceived;
public async Task Connect(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
var telegram = await FetchBusData();
OnTelegramReceived(new TelegramReceivedArgs(telegram.ToArray()));
}
}
private async Task<IEnumerable<byte>> FetchBusData()
{
await Task.Delay(_r.Next(100, 1000));
var buffer = new byte[_r.Next(1, 10)];
_r.NextBytes(buffer);
return buffer;
}
private void OnTelegramReceived(TelegramReceivedArgs e)
{
var handler = TelegramReceived;
if (handler != null) handler(this, e);
}
}
就像我说的,这对我的问题没有帮助
async Task<byte[]> GetTelegramAsync(int length, Timespan timeout)
实现保持不变,还是我遗漏了一点
byte[] GetTelegram(int length, TimeSpan timeout)
{
byte[] telegram = null;
using (var resetEvent = new AutoResetEvent(false))
{
EventHandler<TelegramReceivedArgs> handler = (sender, e) =>
{
var t = e.Telegram;
if (Check(t, length))
{
telegram = t;
resetEvent.Set();
}
};
_d.TelegramReceived += handler;
resetEvent.WaitOne(timeout.Milliseconds);
_d.TelegramReceived -= handler;
}
return telegram ?? new byte[0];
}
async Task<byte[]> GetTelegramAsync(int length, TimeSpan timeout)
{
return await Task.Run(() => GetTelegram(length, timeout));
}
我更新了我的示例,但我无法找出关于 我的问题。当然,我已经解决了线程阻塞的问题 这与我的意思不完全一样,您现在仍然在Task.Delay的帮助下对数据使用pull模型,而不是push模型,在push模型中,通知是异步从总线驱动程序发出的,如图所示 无论如何,我认为下面的实现可能就是您想要的。注意,除了异步I/O总线读取模拟之外,它根本没有显式地使用线程。将真实设备APM API替换为readBus:
此外,此场景似乎是使用实现的理想候选者。如果时间允许,我将演示如何做到这一点。您的总线设备驱动程序是否提供异步API,例如?我有两个可能的I/O源第一个也是FTDI芯片+FTD2XX_网络包装器,第二个I/O源基于WinUSB。我使用的WinUSBNet Wapper实现了一个APM接口,所以我想这可以通过大量的重构来完成。现在这不是一个真正的选项,那么您有什么建议呢?对于这两个源,您都可以选择使用本机异步API。对于使用TaskCompletionSource的第一个,对于使用Task.Factory.FromAsync的第二个。您不需要创建专用线程来驱动此I/O。我更新了我的示例,但我无法找出关于我的问题的区别。当然,我已经解决了阻塞线程的问题。Thx异步任务ReadTelegramAsyncint大小、CancellationToken令牌实现就是我要寻找的。ftd2x_NET和WinUSBnet异步部分非常简单。@nJob,很高兴它有帮助。
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
class Program
{
public class TelegramEventArg: EventArgs
{
public byte[] Data { get; set; }
}
public EventHandler<TelegramEventArg> TelegramEvent = delegate { };
async Task<byte[]> ReadTelegramAsync(int size, CancellationToken token)
{
var tcs = new TaskCompletionSource<byte[]>();
EventHandler<TelegramEventArg> handler = null;
bool subscribed = false;
handler = (s, e) =>
{
if (e.Data.Length == size)
{
this.TelegramEvent -= handler;
subscribed = false;
tcs.TrySetResult(e.Data);
}
};
this.TelegramEvent += handler;
try
{
subscribed = true;
using (token.Register(() => tcs.TrySetCanceled()))
{
await tcs.Task.ConfigureAwait(false);
return tcs.Task.Result;
}
}
finally
{
if (subscribed)
this.TelegramEvent -= handler;
}
}
async Task ReadBusAsync(CancellationToken token)
{
while (true)
{
// get data from the bus
var data = await Task.Factory.FromAsync(
(asyncCallback, asyncState) =>
readBus.BeginInvoke(asyncCallback, asyncState),
(asyncResult) =>
readBus.EndInvoke(asyncResult),
state: null).ConfigureAwait(false);
token.ThrowIfCancellationRequested();
this.TelegramEvent(this, new TelegramEventArg { Data = data });
}
}
// simulate the async bus driver with BeginXXX/EndXXX APM API
static readonly Func<byte[]> readBus = () =>
{
var random = new Random(Environment.TickCount);
Thread.Sleep(random.Next(1, 500));
var data = new byte[random.Next(1, 5)];
Console.WriteLine("A bus message of {0} bytes", data.Length);
return data;
};
static void Main(string[] args)
{
try
{
var program = new Program();
var cts = new CancellationTokenSource(Timeout.Infinite); // cancel in 10s
var task1 = program.ReadTelegramAsync(1, cts.Token);
var task2 = program.ReadTelegramAsync(2, cts.Token);
var task3 = program.ReadTelegramAsync(3, cts.Token);
var busTask = program.ReadBusAsync(cts.Token);
Task.WaitAll(task1, task2, task3);
Console.WriteLine("All telegrams received");
cts.Cancel(); // stop ReadBusAsync
}
catch (Exception ex)
{
while (ex is AggregateException)
ex = ex.InnerException;
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
}
}