Разница между делегатом .BeginInvoke и использованием потоков ThreadPool в С#

В С# есть ли разница между использованием делегата для выполнения асинхронной работы (вызов BeginInvoke()) и использование потока ThreadPool, как показано ниже

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

Edit:
BeginInvoke гарантирует, что поток из пула потоков используется для выполнения асинхронного кода, поэтому есть ли разница?

Ответ 1

Джо Даффи в своей книге Concurrent Programming on Windows (стр. 418) говорит об этом Delegate.BeginInvoke:

Все типы делегатов по соглашению предлагают метод BeginInvoke и EndInvoke наряду с обычным синхронным методом Invoke. Хотя это хорошая функция модели программирования, вы должны держаться подальше от них, где это возможно. В реализации используется удаленная инфраструктура, которая накладывает значительные ажурные накладные расходы на асинхронный вызов. Работа с очередью в пуле потоков напрямую часто является лучшим подходом, хотя это означает, что вы должны сами координировать логику рандеву.

EDIT: я создал следующий простой тест относительных накладных расходов:

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 мс.