74

I have the following code:

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

private void CheckFiles()
{
  //Do stuff
}

I now want to amend CheckFiles to accept and integer and a BlockingCollection reference

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

I can't seem to find a way to Start this task as I did above.

Can you help?

Thanks

Jon
  • 38,814
  • 81
  • 233
  • 382

5 Answers5

121

The best option is probably to use a lambda expression that closes over the variables you want to display.

However, be careful in this case, especially if you're calling this in a loop. (I mention this since your variable is an "ID", and this is common in this situation.) If you close over the variable in the wrong scope, you can get a bug. For details, see Eric Lippert's post on the subject. This typically requires making a temporary:

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

Also, if your code is like the above, you should be careful with using the LongRunning hint - with the default scheduler, this causes each task to get its own dedicated thread instead of using the ThreadPool. If you're creating many tasks, this is likely to have a negative impact as you won't get the advantages of the ThreadPool. It's typically geared for a single, long running task (hence its name), not something that would be implemented to work on an item of a collection, etc.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • Thanks. I will be starting them in a loop. However I do need it as LongRunning. My original program worked with one file but now it needs to do some code on numerous files. Therefore I need to pass in the ID, BlockingCollection, CancellationTokenSoure and StreamReader. I didnt add those extra method parameters in the question however – Jon Nov 14 '11 at 20:15
  • 1
    @Jon: Just make sure to check the scope of those objects... I just mentioned the LongRunning hint as something for you to consider - I will say that its rarely a good idea to use LongRunning, especially in .NET 4, if you're starting many tasks (ie: working in a loop), as the threadpool will typically provide better behavior. – Reed Copsey Nov 14 '11 at 20:19
  • The loop is just to start the tasks. Maximum of 4-6 – Jon Nov 14 '11 at 20:21
  • 1
    I second Reed's comment about LongRunning. I'd be surprised if you see benefit from passing this argument rather than cost. – Adam Ralph Nov 14 '11 at 20:31
  • Are you suggesting that I manually create the threads myself? The tasks are long running. – Jon Nov 15 '11 at 07:15
  • 1
    @Jon No, I'd use tasks, but not flag them long running unless you really need to do so. By default, a Task uses a ThreadPool thread. If you flag them "LongRunning", with the default TaskScheduler, you'll get a dedicated thread made just for that task instead. This is usually not necessary, and, if working with many tasks, bypasses all of the goodness of the ThreadPool, which can impact the behavior of child tasks, etc... – Reed Copsey Nov 15 '11 at 17:49
27
class Program
{
    static void Main(string[] args)
    {
        Task.Factory.StartNew(() => MyMethod("param value"));
    }

    private static void MyMethod(string p)
    {
        Console.WriteLine(p);
    }
}
Fernando Vezzali
  • 2,219
  • 1
  • 29
  • 32
10

For passing a single integer I agree with Reed Copsey's answer. If in the future you are going to pass more complicated constucts I personally like to pass all my variables as an Anonymous Type. It will look something like this:

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
}

You can learn more about it in my blog

Sayed Abolfazl Fatemi
  • 3,678
  • 3
  • 36
  • 48
paulselles
  • 638
  • 6
  • 9
6

Construct the first parameter as an instance of Action, e.g.

var inputID = 123;
var col = new BlockingDataCollection();
var task = Task.Factory.StartNew(
    () => CheckFiles(inputID, col),
    cancelCheckFile.Token,
    TaskCreationOptions.LongRunning,
    TaskScheduler.Default);
Adam Ralph
  • 29,453
  • 4
  • 60
  • 67
1

Try this,

        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;
user3761555
  • 851
  • 10
  • 21