简体   繁体   中英

Need a queue of jobs to be processed by threads

I have some work (a job) that is in a queue (so there a several of them) and I want each job to be processed by a thread.

I was looking at Rx but this is not what I wanted and then came across the parallel task library.

Since my work will be done in an web application I do not want client to be waiting for each job to be finished, so I have done the following:

    public void FromWebClientRequest(int[] ids);
    {
        // I will get the objects for the ids from a repository using a container (UNITY)


       ThreadPool.QueueUserWorkItem(delegate
                                         {
                                             DoSomeWorkInParallel(ids, container);
                                         });
    }

    private static void DoSomeWorkInParallel(int[] ids, container)
    {
        Parallel.ForEach(ids, id=>
                                        {
                                            Some work will be done here...
                                            var respository = container.Resolve...
                                        });


       // Here all the work will be done.
       container.Resolve<ILogger>().Log("finished all work");
    }

I would call the above code on a web request and then the client will not have to wait.

Is this the correct way to do this?

TIA

Another way is to use Tasks:

public static void FromWebClientRequest(int[] ids)
{
    foreach (var id in ids)
    {
        Task.Factory.StartNew(i =>
        {
            Wl(i);
        }
        , id);
    }
}

From the MSDN docs I see that Unitys IContainer Resolve method is not thread safe (or it is not written). This would mean that you need to do that out of the thread loop. Edit: changed to Task .

public void FromWebClientRequest(int[] ids);
{
   IRepoType repoType = container.Resolve<IRepoType>();
   ILogger logger = container.Resolve<ILogger>();
   // remove LongRunning if your operations are not blocking (Ie. read file or download file  long running queries etc)
   // prefer fairness is here to try to complete first the requests that came first, so client are more likely to be able to be served "first come, first served" in case of high CPU use with lot of requests
   Task.Factory.StartNew(() => DoSomeWorkInParallel(ids, repoType, logger), TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
}

private static void DoSomeWorkInParallel(int[] ids, IRepoType repository, ILogger logger)
{
    // if there are blocking operations inside this loop you ought to convert it to tasks with LongRunning
    // why this? to force more threads as usually would be used to run the loop, and try to saturate cpu use, which would be doing nothing most of the time
    // beware of doing this if you work on a non clustered database, since you can saturate it and have a bottleneck there, you should try and see how it handles your workload
    Parallel.ForEach(ids, id=>{
                  // Some work will be done here...
                  // use repository
             });
   logger.Log("finished all work");
}

Plus as fiver stated, if you have.Net 4 then Tasks is the way to go.

Why go Task (question in comment):

If your method fromClientRequest would be fired insanely often, you would fill the thread pool, and overall system performance would probably not be as good as with.Net 4 with fine graining. This is where Task enters the game. Each task is not its own thread but the new.Net 4 thread pool creates enough threads to maximize performance on a system, and you do not need to bother on how many cpus and how much thread context switches would there be.

Some MSDN quotes for ThreadPool:

When all thread pool threads have been assigned to tasks, the thread pool does not immediately begin creating new idle threads. To avoid unnecessarily allocating stack space for threads, it creates new idle threads at intervals. The interval is currently half a second, although it could change in future versions of the .NET Framework.

The thread pool has a default size of 250 worker threads per available processor

Unnecessarily increasing the number of idle threads can also cause performance problems. Stack space must be allocated for each thread. If too many tasks start at the same time, all of them might appear to be slow. Finding the right balance is a performance-tuning issue.

By using Tasks you discard those issues.

Another good thing is you can fine grain the type of operation to run. This is important if your tasks do run blocking operations. This is a case where more threads are to be allocated concurrently since they would mostly wait. ThreadPool cannot achieve this automagically:

Task.Factory.StartNew(() => DoSomeWork(), TaskCreationOptions.LongRunning);

And of course you are able to make it finish on demand without resorting to ManualResetEvent:

var task = Task.Factory.StartNew(() => DoSomeWork());
task.Wait();

Beside this you don't have to change the Parallel.ForEach if you don't expect exceptions or blocking, since it is part of the.Net 4 Task Parallel Library, and (often) works well and optimized on the.Net 4 pool as Tasks do.

However if you do go to Tasks instead of parallel for, remove the LongRunning from the caller Task, since Parallel.For is a blocking operations and Starting tasks (with the fiver loop) is not. But this way you loose the kinda first-come-first-served optimization, or you have to do it on a lot more Tasks (all spawned through ids) which probably would give less correct behaviour. Another option is to wait on all tasks at the end of DoSomeWorkInParallel.

I would call the above code on a web request and then the client will not have to wait.

This will work provided the client does not need an answer (like Ok/Fail).

Is this the correct way to do this?

Almost. You use Parallel.ForEach (TPL) for the jobs but run it from a 'plain' Threadpool job. Better to use a Task for the outer job as well.

Also, handle all exceptions in that outer Task. And be careful about the thread-safety of the container etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM