Stop Threads created with ThreadPool.QueueUserWorkItem that have a specific task

22,974

Solution 1

You could use a CancellationToken:

for (int i = 0; i < 100; i++)
{
    ThreadPool.QueueUserWorkItem(s =>
    {
        Console.WriteLine("Output");
        Thread.Sleep(1000);
    });
}

CancellationTokenSource cts = new CancellationTokenSource();

for (int i = 0; i < 100; i++)
{
    ThreadPool.QueueUserWorkItem(s =>
    {
        CancellationToken token = (CancellationToken) s;
        if (token.IsCancellationRequested)
            return;
        Console.WriteLine("Output2");
        token.WaitHandle.WaitOne(1000);
    }, cts.Token);
}

cts.Cancel();

Solution 2

No, you can't do that. If you want to do something a long the lines then you must write some code to manage it. At the very basic level you need something like this:

object syncObject = new object();
bool shouldOutput2 = true;
ThreadPool.QueueUserWorkItem(s =>
{
    lock(syncObject)
    {
        if(!shouldOutput2)
        {
           return;
        }
    }
    Console.WriteLine("Output2");
    Thread.Sleep(1000);
});

Once you queue the items, then you can set the flag in order to tell the remaining items not to execute:

   lock(syncObject)
   {
        shouldOutput2 = false;
   }

This is a very dirty way of doing it, but it seems like the only way given your example. If you can tell us more about what is the actual real-world behavior you're trying to accomplish, then there could be some better options.

Share:
22,974
maddo7
Author by

maddo7

Updated on July 16, 2022

Comments

  • maddo7
    maddo7 almost 2 years

    Let's say I queue those two methods in a for loop

    for (int i = 0; i < 100; i++)
    {
        ThreadPool.QueueUserWorkItem(s =>
        {
            Console.WriteLine("Output");
            Thread.Sleep(1000);
        });
    }
    
    for (int i = 0; i < 100; i++)
    {
        ThreadPool.QueueUserWorkItem(s =>
        {
            Console.WriteLine("Output2");
            Thread.Sleep(1000);
        });
    }
    

    Is there a way to stop all the threads that output Console.WriteLine("Output2"); but keep the ones running that output Console.WriteLine("Output"); ?

    • Furqan Safdar
      Furqan Safdar over 11 years
      I am not really able to completely understand your requirement. You can just don't start that particular task based on some condition. As you must be needing some condition to decide.
    • Furqan Safdar
      Furqan Safdar over 11 years
      By the way this is method (not thread) that is executed by the available thread in the ThreadPool.
    • Servy
      Servy over 11 years
      What version of C# are you using? Is it 4.0+? If so, you should ideally be using Task objects instead, and it would help greatly at addressing this problem.
    • maddo7
      maddo7 over 11 years
      do you have a resource where I could read about putting like 2k tasks into a pool and then having only 50 tasks run at a time till all tasks are complete like the threadpool?
  • Servy
    Servy over 11 years
    This assumes the OP is on .NET 4.0+, which he may not be (given that he's using the thread pool directly and not tasks). If he is, then this is the way to go.
  • Servy
    Servy over 11 years
    The placement of your lock takes the 100 items previously executed in parallel and runs them serially. The lock should only cover the if.
  • Kiril
    Kiril over 11 years
    @Servy ah, yes! Thanks for that catch, I've moved the execution outside the lock+check.
  • Servy
    Servy over 11 years
    Using a CancellationToken is much cleaner, as it takes care of the synchronization issues for you, but since it's plausible the OP doesn't have .NET 4.0 it's beneficial to have this here.
  • jaket
    jaket over 11 years
    If using < 4.0, the cancellation token could be replaced with a ManualResetEvent.
  • ToolmakerSteve
    ToolmakerSteve over 7 years
    @jaket - (if I understand correctly) ManualResetEvent is for a different situation than CancellationToken. It is used for controlling when a thread STARTS executing. It CAN help with threads that have not yet begun. But it does not help STOP threads that have already begun executing. For that, before .Net 4.0, programmer must write their own code, like Lirik's answer.
  • ToolmakerSteve
    ToolmakerSteve over 7 years
    This answer, as written, only helps if the queued object has not yet started running. To stop a long-running operation containing a loop, place the test inside the loop. while (..) { if (!shouldContinue) return; ... }
  • ToolmakerSteve
    ToolmakerSteve over 7 years
    I just realized Lirik's answer has the same limitation. I see that the question was worded ambiguously. See my comment there for how to adapt the code for stopping a long-running task, before .Net 4.0.
  • jaket
    jaket over 7 years
    @ToolmakerSteve. ManualResetEvent can be used for many purposes beyond starting a thread. In the example above the token.IsCancellationPending could be replaced with mre.WaitOne(0) and the token.WaitHandle.WaitOne(1000) with mre.WaitOne(1000). There is nothing to STOP a thread short of an Abort (which is a horrible idea) other than polling something - like the bool in Links answer, a MRE which is like a waitable bool or a CancellationToken which is more robust and designed specifically for this purpose.