Предоставление синхронных и асинхронных версий метода в C # - PullRequest
4 голосов
/ 28 февраля 2009

Я пишу API на C # и хочу предоставить синхронные и асинхронные версии общедоступных методов. Например, если у меня есть следующая функция:

public int MyFunction(int x, int y)
{
   // do something here
   System.Threading.Thread.Sleep(2000);
   return  x * y;

}

как я могу создать асинхронную версию вышеуказанного метода (возможно, BeginMyFunction и EndMyFunction)? Существуют ли разные способы достижения одного и того же результата и каковы преимущества различных подходов?

Ответы [ 4 ]

7 голосов
/ 28 февраля 2009

Общий подход заключается в использовании delegate:

IAsyncResult BeginMyFunction(AsyncCallback callback)
{
    return BeginMyFunction(callback, null);
}

IAsyncResult BeginMyFunction(AsyncCallback callback, object context)
{
    // Func<int> is just a delegate that matches the method signature,
    // It could be any matching delegate and not necessarily be *generic*
    // This generic solution does not rely on generics ;)
    return new Func<int>(MyFunction).BeginInvoke(callback, context);
}

int EndMyFunction(IAsyncResult result)
{
    return new Func<int>(MyFunction).EndInvoke(result);
}
4 голосов
/ 28 февраля 2009

Мехрдад Афшари отвечает на ваш вопрос как можно лучше. Однако я бы посоветовал против этого, если это вообще возможно. Если ваша бизнес-объект не несет единоличную ответственность за синхронный или асинхронный запуск, вы нарушаете принцип единой ответственности 1002 *, даже пытаясь заставить его осознать тот факт, что он может работать асинхронно. Достаточно просто выполнить этот тип операции в классе потребления с использованием анонимных делегатов:

public void Foo(int x, int y)
{
    ThreadPool.QueueUserWorkItem(delegate
        {
            // code to execute before running
            myObject.MyFunction(x, y);
            // code to execute after running
        });
}

Если у вас нет кода для запуска до или после, вы можете использовать лямбду, чтобы сделать его более кратким

ThreadPool.QueueUserWOrkItem(() => myObject.MyFunction(x, y));

EDIT

В ответ на комментарий @ kshahar ниже, экстернализация асинхронности все еще является хорошей идеей. Это общая проблема, которая решалась с помощью обратных вызовов в течение десятилетий. Лямбды просто сокращают маршрут, а .Net 4.0 делает его еще проще.

public void Foo(int x, int y)
{
    int result = 0; // creates the result to be used later
    var task = Task.Factory.StartNew(() => // creates a new asynchronous task
    {
        // code to execute before running
        result = myObject.MyFunction(x, y);
        // code to execute after running
    });
    // other code
    task.Wait(); // waits for the task to complete before continuing
    // do something with the result.
}

.Net 5 делает это еще проще, но я недостаточно знаком с ним, чтобы сделать заявление, выходящее за рамки этого.

2 голосов
/ 28 февраля 2009

Прежде всего, если вы привязаны к вычислениям, я бы не стал беспокоиться. Оставьте это клиенту, чтобы определить, хотят ли они вызывать вас синхронно в текущем потоке или асинхронно через ThreadPool.QueueUserWorkItem.

Если, однако, у вас есть какая-то форма ввода / вывода в вашей подпрограмме, то может быть полезно предоставить асинхронную версию. Вы должны убедиться, что ваша асинхронная версия использует соответствующие асинхронные вызовы ввода / вывода. Вам также нужно будет реализовать IAsyncResult и вернуть его из вашего вызова BeginMyFunction. См. Реализацию Джо Даффи здесь и некоторые заметки о тонкостях различных реализаций BCL здесь .

1 голос
/ 28 февраля 2009

Вы можете создать версию метода, которая принимает делегата для обратного вызова:

delegate void PassIntDelegate (int i);
delegate void PassIntIntCallbackDelegate (int i1, int i2, PassIntDelegate callback);

public int MyFunction (int i1, int i2)
{
    return i1 * i2;
}

public void MyFunctionAsync (int i1, int i2, PassIntDelegate callback)
{
    new PassIntIntDelegate (_MyFunctionAsync).BeginInvoke (i1, i2, callback);
}

private void _MyFunctionAsync (int i1, int i2, PassIntDelegate callback)
{
    callback.Invoke (MyFunction (i1, i2));
}

Эта версия не так чиста, как та, что использует AsyncCallback, но она немного более безопасна для типов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...