6

我正在维护一些代码,其中包含一个使用任务模式异步调用 WCF 方法的方法的类。

代码实际上如下所示:

public class Manager : IDisposable
{
    public void DoSomething()
    {
        Task<bool> task;

        using (var client = new WcfClient())
        {
            task = client.ReallyDoSomethingAsync(123);
        }
    }
}

管理器本身在另一段代码中的其他地方使用,该代码将对 DoSomething 的调用包装在 using(Manager) 块中。

所以我的问题是,WCF 调用会发生什么。它会发生吗?它放弃了吗?

更一般地说,这是否适用于使用该Task<T>模式的异步调用?如果在异步调用完成之前拥有的类超出范围会发生什么?

4

3 回答 3

6

在敲了一个简短的测试应用程序之后,似乎当一个 WCF 客户端被释放时,它会等待所有异步任务完成后再实际释放。

示例 WCF 服务:

 public class Service1 : IService1
 {
    public string GetData(int value)
    {
        Thread.Sleep(5000);

        return "GOT HERE " + value;
    }
 }

示例客户端:

class Program
{
    static void Main(string[] args)
    {
        using (var wrapper = new Wrapper())
        {
            wrapper.DoSomething();
        }

        Console.WriteLine("Finished.");
        Console.ReadLine();
    }
}

class Wrapper : IDisposable
{
    public void DoSomething()
    {
        Task<string> task1;

        using (var client = new ServiceReference1.Service1Client())
        {
            task1 = client.GetDataAsync(1);
            var task2 = client.GetDataAsync(2);

            Thread.Sleep(1000);

            var task3 = client.GetDataAsync(3);

            Console.WriteLine("Calls started");
        }

        Console.WriteLine("Result of task 1:" + task1.Result);
    }

    public void Dispose()
    {
    }
}

在这种情况下,“呼叫开始”行会在 1 秒延迟后出现。“完成”。在所有三个任务都成功完成之前,不会写入行。

因此,实际上 WCF 服务客户端包装器管理其任务并在处理之前等待完成(或可能是超时)。

...这让我想到:如果您想以“即发即弃”的异步方式调用长时间运行的 WCF 方法,您要么必须在没有using块的情况下执行此操作,要么将整个事情包装在自己的任务中。所以这是很多包装!

于 2013-09-25T11:51:29.750 回答
1

基本上,每个Task都有两个方面:生产者(可以是类似TaskCompletionSource的东西)和消费者(Task本身)。

即使没有对消费者端的引用,仍然有对生产者端的引用。这意味着放弃Task不会做任何事情:异步调用将照常继续。只有在它完成后,Task才会有资格进行垃圾收集。

如果你确实想取消异步操作,你需要明确告诉生产者停止,这通常是通过传递一个CancellationToken.

于 2013-09-25T12:37:56.557 回答
0

并不是专门针对 WCF 的答案(也就是说,这并没有解决“服务是否在离开using块之前实际上触发了方法调用)的问题......但是这个:

Task<int> task;

using (var sr = new StreamReader(File.OpenRead(@"C:\really_big_file.txt"))) {
    task = sr.ReadAsync(new char[2000000], 0, 2000000);
}

Console.WriteLine(task.Result);

将失败Cannot access a closed file。所以,这里似乎没有什么有趣的事情发生......一切都按照你的预期运行。Dispose被调用,并且尝试访问与任务绑定的资源会导致失败。

至于专门检查 WCF 服务调用。我想这只是另一个像这样的简单调试设置来测试它。

回答您关于任务超出范围后是否完成的问题。答案是肯定的。

任务产生线程。如果您生成一个线程,则不必保留对它的引用。ThreadPool 将安排它并运行到完成。

请参阅Thread 文档以了解有关它的概要

具体来说,它说:

启动线程后,不必保留对 Thread 对象的引用。线程继续执行,直到线程过程完成。

于 2013-09-25T09:39:24.600 回答