0

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

所以我可以打电话:

_taskManager.StartNewTask(MethodName);

我需要在一个单独的Task.

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

我的问题是现在我正在尝试实现取消Task,例如如果用户按ESCTask中止。为此,我需要使用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() 能够将 CancellationToken 传递给方法的逻辑,但我不知道该怎么做......
  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(针对问题 n.2) (更改了 CustomMethod)

如果我必须执行类似的方法

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

Task使用我的TaskManager.StartNewTask()方法的新方法中,我应该如何更改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()...的返回类型有关。但是我应该如何管理我在新任务中执行的方法的返回值?怎么了?

4

1 回答 1

1

I guess you simply need Action<CancellationToken> unless I misunderstood.

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

For your question 2: You can use closures to access the surrounding of the method where the anonymous method or lambda is created. Otherwise, how do you plan to pass the n number of arguments? where do you get them from?

You can use closures to workaround this. Jon explains closures here

int result = _taskManager.StartNewTask((ct)=> CustomMethod(paramA, paramB,ct));
于 2014-12-05T09:40:40.610 回答