14

From what I can tell I have misleading bits of information. I need to have a separate thread running in the background.

At the moment I do it like this:

var task = Task.Factory.StartNew
         (CheckFiles
          , cancelCheckFile.Token
          , TaskCreationOptions.LongRunning
          , TaskScheduler.Default);//Check for files on another thread

 private void CheckFiles()
 {
    while (!cancelCheckFile.Token.IsCancellationRequested)
    {
        //do stuff
    }
 }

This always creates a new thread for me. However after several discussions even if it is marked as LongRunning doesn't guarantee that a new thread will be created.

In the past I have done something like this:

thQueueChecker = new Thread(new ThreadStart(CheckQueue));
thQueueChecker.IsBackground = true;
thQueueChecker.Name = "CheckQueues" + DateTime.Now.Ticks.ToString();
thQueueChecker.Start();


private void CheckQueue()
{
   while (!ProgramEnding)
   {
            //do stuff
   }
}

Would you recommend that I go back to this approach to guarantee a new thread is used?

Jon
  • 38,814
  • 81
  • 233
  • 382
  • `LongRunning` is just a hint to the scheduler - if you absolutely must always have a new Thread, you will have to create one. – Nick Butler Nov 07 '11 at 17:30
  • Fancy adding that as an answer? – Jon Nov 07 '11 at 19:44
  • 2
    I think a common point of confusion that non of the answers address, It is not guaranteed to make a new thread, but it will always use a thread from the ThreadPool, it may just reuse a thread that is sitting idle in the pool not doing any work. If you call `Task.Factory.StartNew` from a non ThreadPool thread it will always run the code on a *diffrent* thread, you just don't know if that thread is brand new, or has been used before to run other code. – Scott Chamberlain Oct 08 '13 at 07:22

4 Answers4

19

The default task scheduler ThreadPoolTaskScheduler does indeed always create a new thread for long running task. It does not use the thread pool as you can see. It is no different as the manual approach to create the thread by yourself. In theory it could happen that the thread scheduler of .NET 4.5 does something different but in practice it is unlikely to change.

protected internal override void QueueTask(Task task)
{     
  if ((task.Options & TaskCreationOptions.LongRunning) != TaskCreationOptions.None)
  {
    new Thread(s_longRunningThreadWork) { IsBackground = true }.Start(task);
  }
  else
  {
    bool forceGlobal = 
        (task.Options & TaskCreationOptions.PreferFairness) != TaskCreationOptions.None;
     ThreadPool.UnsafeQueueCustomWorkItem(task, forceGlobal);
  }
}
Alois Kraus
  • 13,229
  • 1
  • 38
  • 64
  • 3
    I fail to see what is wrong with my answer. Could you elaborate what your reasoning was for the downvote? – Alois Kraus Nov 07 '11 at 19:07
  • 4
    @Ramhound I realize this is ancient, but xp != correct answer. Alois is correct, see the .NET Framework sourcecode for `ThreadPoolTaskScheduler` - second method down `QueueTask`: referencesource.microsoft.com/#mscorlib/system/threading/Tasks/ThreadPoolTaskScheduler.cs There's even a comment in the code: // Run LongRunning tasks on their own dedicated thread. – Adam Plocher Jul 22 '14 at 05:56
8

It depends on the Scheduler you use. There are two stock implementations, ThreadPoolTaskScheduler and SynchronizationContextTaskScheduler. The latter doesn't start a thread at all, used by the FromCurrentSynchronizationContext() method.

The ThreadPoolTaskScheduler is what you get. Which indeed uses the LongRunning option, it will use a regular Thread if it set. Important to avoid starving other TP threads. You'll get a TP thread without the option. These are implementation details subject to change without notice, although I'd consider it unlikely anytime soon.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
6

LongRunning is just a hint to the scheduler - if you absolutely must always have a new Thread, you will have to create one.

Nick Butler
  • 24,045
  • 4
  • 49
  • 70
  • 1
    I think this is the correct answer. I like the abstraction of threads using Task but no-one can guarantee a new thread. Henk says I should just let it do what I want it to but this does not guarantee a new thread. – Jon Nov 08 '11 at 09:38
  • Do you think Henk's comments suggesting I'm on the wrong track are valid and if so I'd like to know/learn more – Jon Nov 08 '11 at 17:22
  • I think the confusion is about words like "guarantee". The `LongRunning` option isn't "guaranteed" to do anything at all. However, the default TaskScheduler in .NET 4 does create a new thread and that is *unlikely* to change. Using Tasks is an abstraction that makes the code easier, but you do lose some control. If it really does matter to you, then you could always write a TaskScheduler that guarantees a new Thread and have the best of both worlds :) – Nick Butler Nov 08 '11 at 18:26
  • Interesting idea. I have no problem with creating threads and managing them I just thought the emphasis was now to allow the TPL to do it for you but if it cannot guarantee a new thread then your option/answer is better. I'm also not sure what Henk meant by implying there is a difference between a separate thread and a new thread. Surely its the same unless there is a magical hidden thread waiting for me to issus commands for it to do – Jon Nov 08 '11 at 19:41
  • Here is some information that I have just learnt about ThreadPool and Thread http://stackoverflow.com/questions/230003/thread-vs-threadpool/230023#230023 – Jon Nov 08 '11 at 19:53
  • 2
    There are indeed "magical" hidden threads, as you already found out. But the real question is, why do you want guarantees about how it will be executed? If the task scheduler has reasons to believe a new thread might not be necessary, why would you assume you know better? There's a lot of theory and trade-offs behind decisions like that. – Vladislav Zorov Nov 09 '11 at 12:34
  • Reiterating @scott-chamberlain "I think a common point of confusion.." so it may be read in conjunction- Key point is (if I understood correctly) in theory, LongRunning would enable the Task to run on a ThreadPool thread, not guarantee a *new* thread always - which IMHO is the better way as it would make the best use of system resources i.e. create a new thread only if heuristics determine it to be absolutely necessary – Sudhanshu Mishra Nov 13 '15 at 03:11
4

You'll have to specify why you "always need a separate thread".

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

   task.Wait();
}

A smart scheduler will use 1 thread here. Why shouldn't it?


But in general the CheckFiles() method will be executed on another (than the calling) thread. The issue is whether that thread is especially created or whether it might even be executed on several threads (in succession).

When you are using Tasks you give up control over the Thread. And that should be a good thing.

H H
  • 263,252
  • 30
  • 330
  • 514
  • I want a seperate thread to write to file on another thread so the calling thread is left to do what it wants. I may want another thread to process data received by a socket. In both these circumstances data will be added to a queue and then dequeued on another thread leaving the calling thread free to do other things – Jon Nov 07 '11 at 17:27
  • Then the scheduler will do exactly what you need if you only let it. – H H Nov 07 '11 at 17:29
  • But how can I guaranteed that. How do you know the scheduler will do that for me on another thread? The other thing that I dont specify and that you do is task.Wait. As I want it on another thread I dont Wait() – Jon Nov 07 '11 at 18:30
  • @Jon - you still haven't explained why you "need a new thread". And it got confused with "a separate thread". Anyway you're on the wrong track now. – H H Nov 08 '11 at 10:08
  • If I'm on the wrong track I'd like to start a discussion so I can learn the correct track and improve my understanding. – Jon Nov 08 '11 at 13:09
  • 2
    Example of when you need to guarantee another thread: you get a callback from `SetWindowsHookEx`, and you need to perform a COM call, e.g. the WMI API. If you do it inside the callback, you get an exception: _An outgoing call cannot be made as the application is despatching an input-synchronous call_. You have to *guarantee* that you run whatever it is you need to run on a separate thread. – Roman Starkov Jan 31 '13 at 06:19