C#中delegate.BeginInvoke和使用线程池线程的区别

35
在 C# 中,使用委托异步执行一些工作(调用 BeginInvoke())和使用线程池线程是否有区别?
public void asynchronousWork(object num)
    {
        //asynchronous work to be done
        Console.WriteLine(num);
    }

 public void test()
    {
        Action<object> myCustomDelegate = this.asynchronousWork;
        int x = 7;

        //Using Delegate
        myCustomDelegate.BeginInvoke(7, null, null);

        //Using Threadpool
        ThreadPool.QueueUserWorkItem(new WaitCallback(asynchronousWork), 7);
        Thread.Sleep(2000);
    }

编辑:
BeginInvoke确保使用线程池中的线程来执行异步代码,那么是否有任何区别?


1
可能是Does Func<T>.BeginInvoke use the ThreadPool?的重复问题。 - nothrow
我不确定你的评论是在我编辑问题之前还是之后,但是我知道BeginInvoke会在属于线程池的线程上执行委托。我的问题更多地是关于使用QueueUserWorkItem或BeginInvoke显式地在线程池上执行方法是否有任何区别。 - nighthawk457
1个回答

36

Joe Duffy在他的《Windows并发编程》一书中(第418页)提到了Delegate.BeginInvoke

按照惯例,所有委托类型都会提供一个BeginInvokeEndInvoke方法以及普通的同步Invoke方法。虽然这是一种不错的编程模型特性,但尽可能地避免使用它们是比较好的。实现使用远程调用基础结构,这会对异步调用造成相当大的开销。直接将工作排队到线程池通常是更好的方法,尽管这意味着您必须自行协调会合逻辑。

编辑:我创建了以下关于相对开销的简单测试:

int counter = 0;
int iterations = 1000000;
Action d = () => { Interlocked.Increment(ref counter); };

var stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
for (int i = 0; i < iterations; i++)
{
    var asyncResult = d.BeginInvoke(null, null);
}

do { } while(counter < iterations);
stopwatch.Stop();

Console.WriteLine("Took {0}ms", stopwatch.ElapsedMilliseconds);
Console.ReadLine();

在我的机器上,以上测试需要约20秒运行时间。将 BeginInvoke 调用替换为

System.Threading.ThreadPool.QueueUserWorkItem(state =>
{
    Interlocked.Increment(ref counter);
});

将运行时间更改为864毫秒。


1
我感觉又要出现一种膝反应了?我们需要一些实证数据来客观地描述异步调用的相当大的开销。自己协调会面逻辑与通常被接受的异步实现(例如BeginInvoke、Callback、EndInvoke)的语法糖之间存在什么权衡?使用IAsyncResult的好处之一是我们可以获得一个句柄,从中可以监视正在发生的事情。我从未尝试过使用线程池来看看是否可以做同样的事情。 - IAbstract
1
我想知道为什么每个委托都定义了一个BeginInvoke方法来异步启动委托? BeginInvoke的唯一与任何特定委托签名相关的方面似乎是将委托加参数转换为统一项的过程,如果每个委托定义了一个Bind方法,该方法接受与委托相同的参数并返回一个MethodInvoker,那么这个过程也可以做到。 - supercat
1
@IAbstract - 这里特别讨论的是 Delegate.Begin\EndInvoke,而不是在框架中发现的一般 BeginInvoke\EndInvoke 模式。虽然 IAsyncResult 给你更多的控制权,但它在 .net 4 及以上版本中被放弃,转而使用 Task - Lee
1
@Lee:我在想在旧模式和IAsyncResult的位置上使用Task的问题。感谢您的澄清和一些实证数据! - IAbstract
1
@李: 我对第一项测试 - Action.BeginInvoke(...)得出了一些非常不同的结果:每次运行大约需要10秒,平均每毫秒大约执行100次。ThreadPool排队平均每毫秒执行2.2k次,而Task.Start()平均每毫秒执行1.7k次。如果您希望双重检查我的结果,我可以发布我的测试和相关代码。 ;) 为了保持与您的测试一致,我在我的委托方法中使用相同的Interlocked.Increment(ref counter) - IAbstract

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接