当异步WCF调用的客户端处理或超出范围时,会发生什么情况?

时间:2022-02-14 01:56:29

I am maintaining some code which has a class containing a method that calls a WCF method asynchronously using the Task pattern.

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

The code effectively looks like this:

代码实际上是这样的:

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

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

The manager itself is used somewhere else in another piece of code that wraps the call to DoSomething inside a using(Manager) block.

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

So my question is, what happens to the WCF call. Does it happen? Does it abandon?

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

And more generally, does this hold true for asynchronous calls using the Task<T> pattern? What happens if the owning class goes out of scope before the asynchronous call finishes?

更一般地说,对于使用Task 模式的异步调用,这是否成立?如果拥有类在异步调用结束之前超出范围会发生什么?

3 个解决方案

#1


5  

After knocking-up a brief test app, it seems that when a WCF client is disposed, it waits for all async tasks to complete before it actually disposes.

在删除一个简短的测试应用程序之后,似乎当一个WCF客户端被处理时,它在实际处理之前等待所有异步任务完成。

Example WCF service:

WCF服务示例:

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

        return "GOT HERE " + value;
    }
 }

Example client:

示例客户端:

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()
    {
    }
}

In this scenario, the "Calls started" line appears after the 1 second delay. The "Finished." line does not get written until all the three tasks have completed successfully.

在此场景中,“开始调用”行出现在1秒延迟之后。“完成”行直到三个任务都成功完成才会被写入。

So it does in fact appear that the WCF Service Client wrapper manages its tasks and waits for completion (or presumably timeout) before disposing.

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

... Which gets me thinking: If you want to call a long running WCF method in a "fire and forget" asynchronous manner, you would either have to do it without a using block or wrap the whole thing in its own task. So that's a lot of wrapping!

…这让我想到:如果您想以“触发并忘记”的异步方式调用一个长期运行的WCF方法,您必须在不使用块的情况下进行调用,或者将整个过程封装到自己的任务中。所以这是很多的包装!

#2


1  

Basically, each Task has two sides: the producer (which can be something like TaskCompletionSource) and the consumer (the Task itself).

基本上,每个任务都有两个方面:生产者(可以是TaskCompletionSource)和消费者(任务本身)。

Even when there are no references to the consumer side, there still is a reference to the producer side. What this means is that abandoning the Task won't do anything: the async call will continue as normal. And only after it completes, the Task will become eligible for garbage collection.

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

If you do want to cancel the asynchronous operation, you need to explicitly tell the producer to stop, which is usually done by passing it a CancellationToken.

如果您确实想取消异步操作,您需要显式地告诉生成器停止,这通常是通过传递一个取消令牌来完成的。

#3


0  

Not really an answer for WCF specifically (that is, this doesn't address the question of "Does the service actually fire off the method call prior to leaving the using block).. but this:

并不是WCF的具体答案(也就是说,这并没有解决“服务在离开use块之前是否真的触发了方法调用?”)但这:

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);

Will fail with Cannot access a closed file. So, it would appear that there is no funny business going on here.. and everything runs as you would expect. Dispose is called, and attempting to access the resource tied to the task results in failure.

无法访问已关闭的文件。看来这里并没有什么有趣的事。一切都如你所愿。调用Dispose,尝试访问与任务绑定的资源将导致失败。

As for checking a WCF service call specifically.. I guess it's just another simple debug setup like this to test it.

对于WCF服务调用的检查…我猜这只是另一个简单的调试设置来测试它。

In answer to your question about whether a task completes once it goes out of scope.. the answer is yes.

回答你的问题:一项任务一旦超出范围,它是否就完成了。答案是肯定的。

Tasks spawn threads. If you spawn a thread, you don't have to keep a reference to it. The ThreadPool will schedule it and it will run to completion.

任务生成线程。如果你生成一个线程,你不需要对它进行引用。线程池将调度它,并运行到完成。

See the Thread documentation for a rundown on that.

请参阅线程文档以了解有关这方面的简要说明。

Specifically, it says:

具体地说,它说:

It is not necessary to retain a reference to a Thread object once you have started the thread. The thread continues to execute until the thread procedure is complete.

一旦启动了线程,就没有必要保留对线程对象的引用。线程继续执行,直到线程过程完成。

#1


5  

After knocking-up a brief test app, it seems that when a WCF client is disposed, it waits for all async tasks to complete before it actually disposes.

在删除一个简短的测试应用程序之后,似乎当一个WCF客户端被处理时,它在实际处理之前等待所有异步任务完成。

Example WCF service:

WCF服务示例:

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

        return "GOT HERE " + value;
    }
 }

Example client:

示例客户端:

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()
    {
    }
}

In this scenario, the "Calls started" line appears after the 1 second delay. The "Finished." line does not get written until all the three tasks have completed successfully.

在此场景中,“开始调用”行出现在1秒延迟之后。“完成”行直到三个任务都成功完成才会被写入。

So it does in fact appear that the WCF Service Client wrapper manages its tasks and waits for completion (or presumably timeout) before disposing.

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

... Which gets me thinking: If you want to call a long running WCF method in a "fire and forget" asynchronous manner, you would either have to do it without a using block or wrap the whole thing in its own task. So that's a lot of wrapping!

…这让我想到:如果您想以“触发并忘记”的异步方式调用一个长期运行的WCF方法,您必须在不使用块的情况下进行调用,或者将整个过程封装到自己的任务中。所以这是很多的包装!

#2


1  

Basically, each Task has two sides: the producer (which can be something like TaskCompletionSource) and the consumer (the Task itself).

基本上,每个任务都有两个方面:生产者(可以是TaskCompletionSource)和消费者(任务本身)。

Even when there are no references to the consumer side, there still is a reference to the producer side. What this means is that abandoning the Task won't do anything: the async call will continue as normal. And only after it completes, the Task will become eligible for garbage collection.

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

If you do want to cancel the asynchronous operation, you need to explicitly tell the producer to stop, which is usually done by passing it a CancellationToken.

如果您确实想取消异步操作,您需要显式地告诉生成器停止,这通常是通过传递一个取消令牌来完成的。

#3


0  

Not really an answer for WCF specifically (that is, this doesn't address the question of "Does the service actually fire off the method call prior to leaving the using block).. but this:

并不是WCF的具体答案(也就是说,这并没有解决“服务在离开use块之前是否真的触发了方法调用?”)但这:

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);

Will fail with Cannot access a closed file. So, it would appear that there is no funny business going on here.. and everything runs as you would expect. Dispose is called, and attempting to access the resource tied to the task results in failure.

无法访问已关闭的文件。看来这里并没有什么有趣的事。一切都如你所愿。调用Dispose,尝试访问与任务绑定的资源将导致失败。

As for checking a WCF service call specifically.. I guess it's just another simple debug setup like this to test it.

对于WCF服务调用的检查…我猜这只是另一个简单的调试设置来测试它。

In answer to your question about whether a task completes once it goes out of scope.. the answer is yes.

回答你的问题:一项任务一旦超出范围,它是否就完成了。答案是肯定的。

Tasks spawn threads. If you spawn a thread, you don't have to keep a reference to it. The ThreadPool will schedule it and it will run to completion.

任务生成线程。如果你生成一个线程,你不需要对它进行引用。线程池将调度它,并运行到完成。

See the Thread documentation for a rundown on that.

请参阅线程文档以了解有关这方面的简要说明。

Specifically, it says:

具体地说,它说:

It is not necessary to retain a reference to a Thread object once you have started the thread. The thread continues to execute until the thread procedure is complete.

一旦启动了线程,就没有必要保留对线程对象的引用。线程继续执行,直到线程过程完成。