Using one thread to do a specific task multiple times C #

I have a class that works with a device to avoid delays in the UI layer when working with a device and a serial port. I used a stream, but my device is limited to only one job at a time. So, I have a queue that when the user asks to do something, tasks are added to it, I start a thread to perform tasks one by one.

Every time the user requests a task, I check if the thread is running or not, if so, I just add a new task to the queue if I don't create the thread yet. This means that each temporary queue is empty, I have to create a new thread. Now I want to ask if there is a way to reuse a stream? Since I just need one thread to do the tasks, is it a good idea to use a threadpool? Since suspend is a deprecated method and I don't know when the user will ask another task to use wait (), can I suspend the thread and start it again in some other way? or Is it best to create the thread again and am I doing it right?

public class Modem
{
    Thread thread;
    Queue<Task> Tasks = new Queue<Task>();
    public Modem()
    {
    }

    public void DoTask(Task s)
    {
        Tasks.Enqueue(s);
        if (thread == null || !thread.IsAlive)
           thread = new Thread(HandleTasks);
    }

    private void HandleTasks()
    {
        while (Tasks.Count != 0)
            SendTaskToModem(Tasks.Dequeue());
    }
}

      

+3


source to share


2 answers


Is there a way to reuse a stream? Since I just need one thread to do the tasks - is it a good idea to use a threadpool?

Yes, using ThreadPool

and relying on a framework is generally the best idea, then deploying your own implementation.

What you can do is use one background thread that is responsible for processing your items and another is responsible for their queue. This is a simple producer-consumer problem. To do this, you can use BlockingCollection

:



public class Modem
{
    private BlockingCollection<Task> _tasks;
    public Modem()
    {
       _tasks = new BlockingCollection<Task>();
    }

    public void QueueTask(Task s)
    {
        _tasks.Add(s);
    }

    private Task StartHandleTasks()
    {
         return Task.Run(async () =>
         {
             while (!_tasks.IsCompleted)
             {
                 if (_tasks.Count == 0)
                 {
                     await Task.Delay(100).ConfigureAwait(false);
                     continue;
                 }

                 Task task;
                 _tasks.TryTake(out task);

                 if (task != null)
                 {
                    // Process item.
                 }
             }
         });
    }
}

      

This is a rather simplified use case BlockingCollection

. It has more built-in functionality such as being used CancellationToken

to cancel the item being processed. Please note that you will need to add correct exception handling, cancellation, etc.

Implementing @ChrFin using TPL Dataflow

saves some overhead when starting task processing and spinning the background thread until there are no items to process. I posted this answer to give you another way to solve your problem.

+2


source


There is something built in for such tasks: ActionBlock

(not redistributable with the core framework, you need to add a Microsoft.Tpl.Dataflow

NuGet Package).
If you create it like:

var actionBlock = new ActionBlock<TASK>(t => DoWork(t), 
    new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = 1 });

      

only one item is processed.

Then you just call:



actionBlock.Post(yourTask);

      

and all "submitted" tasks are executed one by one in their own thread.

When you're done, you can call actionBlock.Complete();

and then for example await actionBlock.Completion;

. There you can also check for exceptions that happen internally DoWork

.

+2


source







All Articles