Как связать функцию с аргументами в пакет в C # - PullRequest
0 голосов
/ 28 августа 2018

Я пишу пул потоков, используя C #.

Этот пул потоков должен поддерживать выполнение функций другого типа.

Вот что мне нужно:

  1. связывает функцию и ее аргументы в функторе (или, скажем, вызываемом объекте), и ее подпись должна быть void (void)
  2. получить System.Threading.Tasks< TResult >, представляющее возвращаемое значение нашего вызываемого пакета

В CPP я могу сделать это с помощью некоторой магии шаблона:

template <typename funtype, typename ...argstype>
std::future<typename std::result_of<funtype(argstype...)>::type> async(funtype&& func, argstype&&... args) {
    //start function body↓

    typedef std::packaged_task<std::result_of<funtype(argstype...)>::type(argstype...)> task_type;
    auto task = std::make_shared<task_type>(std::forward<funtype>(func));

    // bind to a callable object(functor) with signature void(void)
    auto whatINeed= std::bind([task](argstype... args) mutable {
    (*task)(std::forward<argstype>(args)...);
    }, std::forward<argstype>(args)...);

    //and we return the std::future which represents the return value of our package
    //in C#, i need to return an Task<TResult>
    return task->get_future();
}

В C # теперь я написал:

public Task<TResult> async<TResult>(Delegate func, params object[] args)
{
    var stdPromiseXD = new TaskCompletionSource<TResult>();
    // the lambda is a callable object with signature void(void)
    works.Enqueue(() =>
    {
        try
        {
            stdPromiseXD.SetResult((TResult)func.DynamicInvoke(args));
        }
        catch (Exception ex)
        {
            stdPromiseXD.SetException(ex);
        }
    });
    // return the Task which equals std::future in CPP
    return stdPromiseXD.Task;
}

Но эта версия C # не так хороша, как версия CPP. Во-первых, он не поддерживает функцию без возврата, во-вторых, метод DynamicInvoke в некоторых ситуациях может быть значительно медленным.

Так может ли кто-нибудь сказать мне, как связать функцию и аргументы в пак в C # более элегантно?

1 Ответ

0 голосов
/ 29 августа 2018

Я рекомендую использовать Func<TResult> и Action вместо делегата, а затем использовать замыкания в вызывающем коде для упрощения использования. Func - универсальный строго типизированный делегат, который возвращает результат, а Action - универсальный строго типизированный делегат, который не возвращает результат.

public Task<TResult> Enqueue<TResult>(Func<TResult> func)
{
    var stdPromiseXD = new TaskCompletionSource<TResult>();
    // the lambda is a callable object with signature void(void)
    works.Enqueue(() =>
    {
        try
        {
            stdPromiseXD.SetResult((TResult)func());
        }
        catch (Exception ex)
        {
            stdPromiseXD.SetException(ex);
        }
    });
    // return the Task which equals std::future in CPP
    return stdPromiseXD.Task;
}

public Task Enqueue(Action action)
{
    return Enqueue<object>(() =>
    {
        action();
        return null;
    });
}

Я назвал это с:

var arg1 = "x1";
var arg2 = "2nd";
var arg3 = "third";

var resultTask1 = tp.Enqueue(() => DoConsoleWrite(arg1, arg2, arg3));
var resultTask2 = tp.Enqueue(() => SumAllNumbers(1, 2, 3, 4, 5));
var resultTask3 = tp.Enqueue(() => ThrowException());

while (tp.Pop()) { }

resultTask1.GetAwaiter().GetResult();
var result2 = resultTask2.GetAwaiter().GetResult();
var result3Exception = resultTask3.Exception;

Альтернативой использованию замыканий является создание перегрузок для каждого счетчика параметров функции (Func<TResult>, Func<T1,TResult>, Func<T1,T2,Result>, etc и Action, Action<T1>, Action<T1,T2>, etc)

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