【问题标题】:How can I use WaitHandle awaiting completion of an asynchronous call?如何使用 WaitHandle 等待异步调用完成?
【发布时间】:2012-12-27 14:13:53
【问题描述】:

考虑这段代码:

class Program
        {
            static void Main(string[] args)
            {
                Master master = new Master();
                master.Execute();
            }
        }

    class TestClass
    {
        public void Method(string s)
        {
            Console.WriteLine(s);
            Thread.Sleep(5000);
            Console.WriteLine("End Method()");
        }
    }
    class Master
    {
        private readonly TestClass test = new TestClass();

        public void Execute()
        {
            Console.WriteLine("Start main thread..");
            Action<String> act = test.Method;
            IAsyncResult res = act.BeginInvoke("Start Method()..", x =>
            {
                Console.WriteLine("Start Callback..");
                act.EndInvoke(x);
                Console.WriteLine("End Callback");
            }, null);
            Console.WriteLine("End main thread");
            Console.ReadLine();
        }
    }

我们有结果:

Start main thread..
End main thread
Start Method()..
End Method()
Start Callback..
End Callback

所以,我想要结果:

Start main thread..    
Start Method()..
End Method()
Start Callback..
End Callback
End main thread

如何在此代码中等待async?我查看了 MSDN 文章"Calling Synchronous Methods Asynchronously" 发现了这个:

拨打BeginInvoke后,您可以执行以下操作:

  • 做一些工作,然后调用EndInvoke阻止直到调用 完成。
  • 使用IAsyncResultAsyncWaitHandle获取WaitHandle
    属性,使用其WaitOne 方法阻止执行,直到
    WaitHandle 发出信号,然后调用EndInvoke
  • 轮询BeginInvoke 返回的IAsyncResult 以确定何时 异步调用完成,然后调用EndInvoke
  • 将回调方法的委托传递给BeginInvoke。方法 异步调用时在ThreadPool线程上执行 完成。回调方法调用EndInvoke

我认为这一秒对我来说更好。但是如何实现呢?特别是我对重载WaitOne() (Blocks the current thread until the current WaitHandle receives a signal) 很感兴趣。如何正确地做到这一点?我的意思是这种情况下的常见模式。

更新:

现在我用Task&lt;T&gt;

 class Program
    {
        static void Main(string[] args)
        {
            Master master = new Master();
            master.Execute();
        }
    }

    class WebService
    {
        public int GetResponse(int i)
        {
            Random rand = new Random();
            i = i + rand.Next();
            Console.WriteLine("Start GetResponse()");
            Thread.Sleep(3000);
            Console.WriteLine("End GetResponse()");
            return i;
        }

        public void SomeMethod(List<int> list)
        {
            //Some work with list        
            Console.WriteLine("List.Count = {0}", list.Count);
        }
    }
    class Master
    {
        private readonly WebService webService = new WebService();

        public void Execute()
        {
            Console.WriteLine("Start main thread..");
            List<int> listResponse = new List<int>();
            for (int i = 0; i < 5; i++)
            {
                var task = Task<int>.Factory.StartNew(() => webService.GetResponse(1))
                    .ContinueWith(x =>
                    {
                        Console.WriteLine("Start Callback..");
                        listResponse.Add(x.Result);
                        Console.WriteLine("End Callback");
                    });
            }
            webService.SomeMethod(listResponse);
            Console.WriteLine("End main thread..");
            Console.ReadLine();
        }
    }

主要问题是 SomeMethod() 变为空 list

结果:

现在我有一个可怕的解决方案:(

 public void Execute()
        {
            Console.WriteLine("Start main thread..");
            List<int> listResponse = new List<int>();
            int count = 0;
            for (int i = 0; i < 5; i++)
            {
                var task = Task<int>.Factory.StartNew(() => webService.GetResponse(1))
                    .ContinueWith(x =>
                    {
                        Console.WriteLine("Start Callback..");
                        listResponse.Add(x.Result);
                        Console.WriteLine("End Callback");
                        count++;
                        if (count == 5)
                        {
                            webService.SomeMethod(listResponse);
                        }
                    });

            }
            Console.WriteLine("End main thread..");
            Console.ReadLine();
        }

结果:

这就是我等待异步调用所需要的。我如何在这里使用Wait 代替Task

更新 2:

class Master
{
    private readonly WebService webService = new WebService();
    public delegate int GetResponseDelegate(int i);

    public void Execute()
    {
        Console.WriteLine("Start main thread..");
        GetResponseDelegate act = webService.GetResponse;
        List<int> listRequests = new List<int>();
        for (int i = 0; i < 5; i++)
        {
            act.BeginInvoke(1, (result =>
            {                    
                int req = act.EndInvoke(result);
                listRequests.Add(req);
            }), null);  
        }

        webService.SomeMethod(listRequests);
        Console.WriteLine("End main thread..");
        Console.ReadLine();
    }
}

【问题讨论】:

    标签: c# asynchronous begininvoke waithandle waitone


    【解决方案1】:

    我想这就是你想要的。我们正在开始任务,然后等待它们完成,所有完成后,我们正在获得结果。

    class Program
    {
        static void Main(string[] args)
        {
            Master master = new Master();
            master.Execute();
        }
    }
    
    class WebService
    {
        public int GetResponse(int i)
        {
            Random rand = new Random();
            i = i + rand.Next();
            Console.WriteLine("Start GetResponse()");
            Thread.Sleep(3000);
            Console.WriteLine("End GetResponse()");
            return i;
        }
    
        public void SomeMethod(List<int> list)
        {
            //Some work with list        
            Console.WriteLine("List.Count = {0}", list.Count);
        }
    }
    class Master
    {
        private readonly WebService webService = new WebService();
        public void Execute()
        {
            Console.WriteLine("Start main thread..");
            var taskList = new List<Task<int>>();
            for (int i = 0; i < 5; i++)
            {
                Task<int> task = Task.Factory.StartNew(() => webService.GetResponse(1));
                taskList.Add(task);
            }
    
            Task<List<int>> continueWhenAll =
                Task.Factory.ContinueWhenAll(taskList.ToArray(),
                                tasks => tasks.Select(task => task.Result).ToList());
    
            webService.SomeMethod(continueWhenAll.Result);
            Console.WriteLine("End main thread..");
            Console.ReadLine();
        }
    
    }
    

    BeginIvoke/EndInvoke 的丑陋解决方案

    public void Execute()
    {
        Func<int, int> func = webService.GetResponce;
    
        var countdownEvent = new CountdownEvent(5);
        var res = new List<int>();
        for (int i = 0; i < 5; ++i)
        {
            func.BeginInvoke(1, ar =>
                            {
                                var asyncDelegate = (Func<int, int>)((AsyncResult)ar).AsyncDelegate;
                                int ii = asyncDelegate.EndInvoke(ar);
                                res.Add(ii);
                                ((CountdownEvent)((AsyncResult)ar).AsyncState).Signal();
                            }, countdownEvent);
        }
        countdownEvent.Wait();
        Console.WriteLine(res.Count);
    }
    

    【讨论】:

    • 感谢您的回复!看来这就是我需要的! :)
    • 哈姆雷特,你能看看我的更新2吗?我对学术目的感兴趣,如果使用 async BeginInvoke() 是否可以达到与您的回复相同的结果?
    【解决方案2】:

    从您的示例代码中不清楚为什么需要异步调用。如果你不这样做,你可以同步调用test.Method();

    假设您需要异步执行,请不要为过时的 Delegate.BeginInvoke 东西烦恼。使用基于 Task 的新 API:

    var task = Task.Factory.StartNew(() => test.Method());
    

    然后您可以在任务上Waitawait 它或使用Task.ContinueWith(根据您的情况选择适当的技术)。

    【讨论】:

    • 用户,感谢您的反馈!真的,我给出了错误的示例代码。我使用 Windows Phone 7.5 和对 Web 服务的异步请求。在示例代码中我隐藏了这一刻,因为我想要示例中的简单代码。我的帖子的更新与现实代码最相似。我使用基于任务的 API,它在 Windows Phone 7 中可用:)
    • 现在这是一个完全不同的问题......看起来你需要一个关于并发如何工作的好教程。您的代码不安全,可以从一些最佳实践中受益。我建议使用教程材料来学习基础知识。
    猜你喜欢
    • 1970-01-01
    • 2011-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-04
    • 2019-10-30
    • 1970-01-01
    相关资源
    最近更新 更多