使用Task.Factory.StartNew传递方法参数

75

我有如下代码:

var task = Task.Factory.StartNew(CheckFiles, cancelCheckFile.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);

private void CheckFiles()
{
  //Do stuff
}

我现在想修改CheckFiles函数,使其能够接受一个整数和一个BlockingCollection引用。

private void CheckFiles(int InputID, BlockingCollection<string> BlockingDataCollection)
{
  //Do stuff
}

我似乎找不到像我上面所做的那样开始这个任务的方法。

你能帮忙吗?

谢谢

5个回答

122

最好的选择可能是使用一个 lambda 表达式,它关闭了你想要显示的变量。

然而,在这种情况下要小心,特别是如果你在循环中调用它。(我之所以提到这一点,是因为你的变量是一个“ID”,在这种情况下很常见。) 如果你在错误的作用域中关闭变量,你可能会遇到 bug。有关详细信息,请参见Eric Lippert 在此主题上的帖子。这通常需要制作一个临时副本:

foreach(int id in myIdsToCheck)
{
    int tempId = id; // Make a temporary here!
    Task.Factory.StartNew( () => CheckFiles(tempId, theBlockingCollection),
         cancelCheckFile.Token, 
         TaskCreationOptions.LongRunning, 
         TaskScheduler.Default);
}

此外,如果您的代码类似于上面的示例,那么使用LongRunning提示时要小心 - 默认调度程序会导致每个任务都有自己的专用线程,而不是使用线程池。 如果您正在创建许多任务,则可能会产生负面影响,因为您将无法获得线程池的优势。 它通常适用于单个长时间运行的任务(因此它的名称),而不是针对集合项等实现的任务。

谢谢。我将在循环中开始它们。但是我确实需要它作为LongRunning。我的原始程序可以处理一个文件,但现在需要对多个文件进行一些代码处理。因此,我需要传入ID、BlockingCollection、CancellationTokenSoure和StreamReader。我没有在问题中添加这些额外的方法参数。 - Jon
1
@Jon:只需确保检查这些对象的范围...我只是提到LongRunning提示作为您考虑的内容 - 我会说,特别是在.NET 4中,如果您正在启动许多任务(即:在循环中工作),使用LongRunning通常不是一个好主意,因为线程池通常会提供更好的行为。 - Reed Copsey
循环只是为了启动任务。最多4-6个。 - Jon
1
我赞同Reed关于LongRunning的评论。如果你传递这个参数而不是成本,我会感到惊讶如果你看到好处。 - Adam Ralph
你是在建议我手动创建线程吗?这些任务需要长时间运行。 - Jon
1
@Jon 不,我会使用任务,但不会将其标记为“长时间运行”,除非您确实需要这样做。默认情况下,任务使用线程池线程。如果您将它们标记为“LongRunning”,则会使用专门的线程仅为该任务而创建。这通常是不必要的,如果处理许多任务,则会绕过线程池的所有好处,这可能会影响子任务的行为等... - Reed Copsey

27
class Program
{
    static void Main(string[] args)
    {
        Task.Factory.StartNew(() => MyMethod("param value"));
    }

    private static void MyMethod(string p)
    {
        Console.WriteLine(p);
    }
}

10

如果只传递一个整数,我同意Reed Copsey的答案。但如果将来你需要传递更复杂的结构,我个人喜欢将所有变量作为匿名类型进行传递。代码看起来会像这样:

foreach(int id in myIdsToCheck)
{
    Task.Factory.StartNew( (Object obj) => 
        {
           var data = (dynamic)obj;
           CheckFiles(data.id, theBlockingCollection,
               cancelCheckFile.Token, 
               TaskCreationOptions.LongRunning, 
               TaskScheduler.Default);
        }, new { id = id }); // Parameter value
}

你可以在我的博客中了解更多相关内容。


6

将第一个参数构造为 Action 的一个实例,例如:

var inputID = 123;
var col = new BlockingDataCollection();
var task = Task.Factory.StartNew(
    () => CheckFiles(inputID, col),
    cancelCheckFile.Token,
    TaskCreationOptions.LongRunning,
    TaskScheduler.Default);

如果inputID的值在其他地方被更改了怎么办?如果方法被第二次调用会怎样? - Martin Meeser

1

试一试这个,

        var arg = new { i = 123, j = 456 };
        var task = new TaskFactory().StartNew(new Func<dynamic, int>((argument) =>
        {
            dynamic x = argument.i * argument.j;
            return x;
        }), arg, CancellationToken.None, TaskCreationOptions.AttachedToParent, TaskScheduler.Default);
        task.Wait();
        var result = task.Result;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接