Task.Factory.StartNew 的包装方法,用于执行具有不同参数和返回值的自定义方法

本文关键字:参数 自定义方法 返回值 执行 StartNew Factory 包装 方法 用于 Task | 更新日期: 2023-09-27 18:33:23

我正在用 .NET 4.0C# 中开发一个 MDI 应用程序。我创建了一个包装类TaskManager来管理某些方法在单独任务上的执行。

所以我可以打电话:

_taskManager.StartNewTask(MethodName);

在分离的Task上,我需要执行每种紧张的工作方法。

我创建了这个包装类,以避免对分散在代码周围的Task.Factory.StartNew()调用,从而保持它干净,并且能够以某种方式跟踪线程。

我的问题是现在我正在尝试实现 取消Task ,例如如果用户按 ESC 键Task中止。为此,我需要使用该CancellationToken并将其作为参数添加到我的所有方法中。然后必须在每个方法主体中检查它。例如:

private void MethodName(CancellationToken ct)
{
    // Verify cancellation request
    if (ct.IsCancellationRequested)
    {
        // Log the cancellation request "The task was cancelled before it got started"
        ct.ThrowIfCancellationRequested();
    }
    // Do the heavy work here
    // ...
    // At some critic point check again the cancellation request
    if (ct.IsCancellationRequested)
    {
        // Log the cancellation request "The task was cancelled while still running"
        ct.ThrowIfCancellationRequested();
    }
}

现在,我TaskManager.StartNewTask()逻辑是这样的:

public int StartNewTask(Action method)
{
    try
    {
        CancellationToken ct = _cts.Token;
        Task task = Task.Factory.StartNew(method, ct);
        _tasksCount++;
        _tasksList.Add(task.Id, task);
        return task.Id;
    }
    catch (Exception ex)
    {
        _logger.Error("Cannot execute task.", ex);
    }
    return -1;
}

我想要什么:

  1. 我需要更改TaskManager.StartNewTask()的逻辑才能将取消令牌传递给该方法,但我不知道该怎么做......
  2. 我还想知道是否有可能创建一个更通用TaskManager.StartNewTask()方法,该方法可以执行具有任意数量的输入参数任何类型的返回值的任何类型的方法。

我需要这样的东西:

// I don't know ho to change the method signature to accept
// methods with parameters as parameter...
public int StartNewTask(Action method)
{
    try
    {
        CancellationToken ct = _cts.Token;
        // Here I need to pass the CancellationToken back to the method
        // I know that this can't be the way...
        Task task = Task.Factory.StartNew(method(ct), ct);
        _tasksCount++;
        _tasksList.Add(task.Id, task);
        return task.Id;
    }
    catch (Exception ex)
    {
        _logger.Error("Cannot execute task.", ex);
    }
    return -1;
}

更新1(关于问题2((更改了自定义方法(

如果我必须执行这样的方法

int CustomMethod (int a, int b, CancellationToken ct)

在使用TaskManager.StartNewTask()方法的新Task中,我应该如何更改StartNewTask()以及如何进行调用?

类似的东西

int result = taskManager.StartNewTask(CustomMethod(<input parameters here>));

代码可能是这样的

public partial class MyForm : Form
{
    private readonly TaskManager _taskManager;
    public MyForm()
    {
        InitializeComponent();
        _taskManager = TaskManager.GetInstance();
    }
    private void btnOK_Click(object sender, EventArgs e)
    {
        // This is the call to StartNewTask()
        // where now I need to set the parameters for CustomMethod()
        // Input parameters could be class variables or a custom object
        // with specific Properties such as:
        // MyObject.MyString, MyObject.MyDouble
        int result = _taskManager.StartNewTask(CustomMethod<input parameters here>);
        // Do something with my result...
        MessageBox.Show("Result: " + result, "Operation", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }
    private int CustomMethod(int a, int b, CancellationToken ct)
    {
        if (ct.IsCancellationRequested)
        {
            ct.ThrowIfCancellationRequested();
        }
        int result = -1;
        // Do some heavy work with int 'a', int 'b' and produce result...
        // Meanwhile check again for cancel request
        return a + b;
    }
}

更新 2

在为我的问题 2 尝试@Sriram Sakthivel 建议后,我现在有以下代码:

private void btnOK_Click(object sender, EventArgs e)
{
    // a=1 and b=3
    int result = _taskManager.StartNewTask((ct) => CustomMethod(1, 3, ct));
    // On the MessageBox I get 2...
    MessageBox.Show("Result: " + result, "Operation", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private int CustomMethod(int a, int b, CancellationToken ct)
{
    if (ct.IsCancellationRequested)
    {
        ct.ThrowIfCancellationRequested();
    }
    // a=1 and b=3, so the sum must me 4...
    return a + b;
}
public class TaskManager
{
    private static readonly TaskManager Instance = new TaskManager();
    private readonly Dictionary<int, Task> _tasksList;
    private static int _tasksCount;
    private static CancellationTokenSource _cts;
    public int StartNewTask(Action<CancellationToken> method)
    {
        try
        {
            CancellationToken ct = _cts.Token;
            Task task = Task.Factory.StartNew(() => method, ct);
            _tasksCount++;
            _tasksList.Add(task.Id, task);
            return task.Id;
        }
        catch (Exception ex)
        {
            _logger.Error("Cannot execute the task.", ex);
        }
        return -1;
    }
}

它让我回到 2 ... 但是 a = 1 和 b = 3... 所以总和应该是 4!

这可能与TaskManager.StartNewTask()的返回类型有关...但是,我应该如何管理在新任务中执行的方法的返回值呢?怎么了?

Task.Factory.StartNew 的包装方法,用于执行具有不同参数和返回值的自定义方法

我想你只需要Action<CancellationToken>,除非我误解了。

public int StartNewTask(Action<CancellationToken> method)
{
    try
    {
        CancellationToken ct = _cts.Token;
        Task task = Task.Factory.StartNew(method(ct), ct);
        _tasksCount++;
        _tasksList.Add(task.Id, task);
        return task.Id;
    }
    catch (Exception ex)
    {
        _logger.Error("Cannot execute task.", ex);
    }
    return -1;
}

对于您的问题 2:您可以使用闭包来访问创建匿名方法或 lambda 的方法的周围环境。否则,您打算如何传递 n 个参数?你从哪里得到它们?

您可以使用闭包来解决此问题。乔恩在这里解释闭包

int result = _taskManager.StartNewTask((ct)=> CustomMethod(paramA, paramB,ct));