1

我创建了一个 WCF 服务应用程序,除了名称之外基本上没有更改,默认合同在那里,并包含一个GetData(Int32)返回字符串的方法。

然后,我创建了面向.NET FW 4.5的WPF 客户端应用程序,因此在向所述服务添加服务引用的地方,我可以选择在生成的服务客户端中包含基于任务的合同方法的异步变体。

现在,当我尝试使用该服务时,如下所示:

  using (var client = new RhaeoServiceClient())
  {
    client.GetDataAsync(17).ContinueWith(t => MessageBox.Show(t.Result));
    MessageBox.Show("inb4");
  }

当从按钮单击处理程序执行时,窗口停止,按钮保持按下状态一秒钟左右,然后"inb4" 显示消息,所以在我看来,任务在主线程上运行并等待网络,从而冻结 UI。

单击后不会立即显示"inb4",它似乎在任务执行后等待,就像我说的,一两秒。显示后"inb4",下一个带有结果的消息框也会显示,但对我来说有趣的是,下一个消息框不会等我关闭第一个,它只是在第一个显示后几乎立即弹出第一个。

所以这很令人困惑,因为它看起来好像延续代码实际上是在不同的线程中运行的,并且并不关心主线程被第一个消息框阻塞。但是它如何显示只能从 UI 线程显示的消息框(对吗?)?

为什么第一个消息框在任务执行后等待,然后显示,然后被下一个消息框覆盖而不被关闭?

4

1 回答 1

0

t.Result 将阻塞调用线程,直到 Task 完成。您可以通过将 async/await 关键字与 WCF 调用结合使用来获得所需的结果。

    private void button1_Click(object sender, EventArgs e)
    {
        CallGetDataAsync(17);
        MessageBox.Show("inb4");
    }

    private async void CallGetDataAsync(int number)
    {
        string result = null;

        var client = new Service1Client();
        try
        {
            // After this line, control is returned to the calling method; the ConfigureAwait(true) 
            // explicitly indicates that when execution resumes, it should attempt to marshall back
            // to the calling thread.  If you change it to false, you can see that the subsequent
            // messagebox does not stop you from interacting with your main form.
            result = await client.GetDataAsync(number).ConfigureAwait(true);

            // when the async service call completes, execution will resume here
            client.Close();
        }
        catch
        {
            try
            {
                client.Close();
            }
            catch
            {
                client.Abort();
            }
            throw;
        }

        // display the MessageBox, this should block the UI thread 
        MessageBox.Show(result);
    }

针对与客户端在同一台机器上运行的服务运行此操作,很难看到发生了什么,因为 WCF 服务调用将返回足够快,以至于在服务结果消息之后仍然显示您的“inb4”。在示例服务方法中添加延迟有助于更好地说明行为。

    public string GetData(int value)
    {
        return Task.Delay(TimeSpan.FromSeconds(5)).ContinueWith(_ => string.Format("You entered: {0}", value)).Result;
    }

对于您的最后一个问题,后台线程可以调用 MessageBox。但是,如果这样做,它不会充当模式并阻止您的主窗体。

于 2013-10-05T23:15:08.210 回答