【发布时间】:2014-12-10 17:43:59
【问题描述】:
我正在使用 .NET 4.0 在 C# 中开发一个 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;
}
我想要什么:
-
我需要更改
TaskManager.StartNewTask()的逻辑才能将CancellationToken传递给方法但我不知道该怎么做... - 我还想知道是否有可能创建一个更更通用
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) (更改了自定义方法)
如果我必须执行类似的方法
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() 的返回类型有关...但是我应该如何管理我在新任务中执行的方法的返回值?怎么了?
【问题讨论】:
-
看看你从
StartNewTask方法返回了什么?您返回TaskId而不是计算结果。要启用返回值,您需要Func<CancellationToken, int>而不是Action<CancellationToken>。 -
是的,没错。但是现在,我该如何管理 Task Id 的返回以及我正在运行的方法的返回值?
-
创建一个新类来存储两者并返回其实例,或使用输出参数。我建议第一个选项。
-
阅读 (here) 我找到了一种通过使用
Task<type>(Task<int>等) 从任务内部运行的方法获取结果的方法。所以电话应该是:Task<int> myTask = Task<int>.Factory.StartNew(method, ct)。现在,如果我想让我的TaskManager.StartNewTask()更通用并且能够接受任何类型的方法作为参数并返回该方法返回的任何类型的值,我应该如何继续?类似于: public TaskStartNewTask(Func method)...
标签: c# multithreading task-parallel-library task cancellation-token