Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/290.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 用于长时间运行同步方法的异步方法_C#_Asynchronous_Task Parallel Library_Async Await_C# 5.0 - Fatal编程技术网

C# 用于长时间运行同步方法的异步方法

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 更新: 好的,我将我的示例更改为异步方法,但我仍然不知道这将如何帮助我解决问题?好吧,好的一面是,我不再有线程在大部分时间被阻塞,但这不是我的问题: 所以我试着用另一种方式来解释。我想要一个异步方法,在总线上侦听并返回与定义

大家好,我对所有异步的东西都很陌生,如果你们能给我一些建议,那就太好了。我不确定我的方法是否可行

假设我有一个总线设备正在读取数据,如果电报完成,它就会触发一个事件。现在我想检查每封电报的长度。如果长度==期望值->如果不尝试,则单击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();
        }
    }
}