Proper way to use C# Tasks?

Hi,

I am trying to get clarification on how to use C# Tasks.

Lets say I have 10,000 objects I want to process.

Is it sensible to create 10,000 Task objects with the Task.Factory?

https://msdn.microsoft.com/en-us/library/dd321439(v=vs.110).aspx
TaskFactory.StartNew Method (Action)

Or should I be tiring to re-use a limited number?

However from the doco I am not sure how to do this. (Sure it something I have missed).

I have had some problems with things in the WaitingToRun state for longer then I would of expected.

So I am hoping to get some general guidance on all of this. (And some examples if possible).

Thanks,
LVL 1
whorsfallAsked:
Who is Participating?

Improve company productivity with a Business Account.Sign Up

x
 
ste5anConnect With a Mentor Senior DeveloperCommented:
It depends on the kind of tasks to perform and your machine, where you run those 10k tasks.

But generally speaking: No, its normally not a good idea.

Most for the reason that you may run you program on different machines, on capable of running only 50 tasks and another of running 500 tasks.

The normal approaches I use in such cases:

1) Use a blocking queue to store the task description. Use a single thread to spawn new threads when new task descriptions arrive and fewer then the maximum worker threads are alive.

Advantage: Each thread and its description are isolated from each other. State of threads does not matter.
Disadvantage: Needs some more time to instantiate.

Usage: When isolation is more important than speed. Like high-level jobs as print or download jobs.

2) Use a blocking queue to store the task description. Create a configurable amount of tasks. Each task takes its task description from the queue. When no more description is available, the tasks quits.

Advantage: Lesser time to instantiate.
Disadvantage: Threads keep state.
Usage: Low-level jobs like object processing for drawing graphics.

And then try to find a good number for your worker threads. E.g. for download tasks the bandwidth of your server is the bottleneck. Here even 100 or lesser tasks are optimal to balance memory and CPU consumption due to low bandwidth waits. Make this a configuration parameter in your app.config.
0
 
whorsfallAuthor Commented:
Hi ste5an,

Thanks for your response. Unless I am mistaken is there not some parameter you can get from the threadpool that says how many tasks you can run and the same time. And if you run more it block you?

ThreadPool.GetMaxThreads Method (Int32, Int32) --> Should I be looking at this figure ?

Also I have got some of my code below.

Is this kind of the right way. the function fnCreate3 deques data from the fake_pc_queue.


Thanks,

Ward

        private void fnRun_Queue(BlockingCollection<Fake_PC> processing_list,
                                 BlockingCollection<Fake_PC> fake_pc_results,
                                 BlockingCollection<Fake_PC> pc_error_list,
                                 Task_Stats my_stats,
                                 int number_of_threads,
                                 int iteration_count)
        {
            ConcurrentQueue<Fake_PC> fake_pc_queue = new ConcurrentQueue<Fake_PC>();
            int total_machines = processing_list.Count;
            int queue_count = 0;
            int results_count = 0;
            int error_count = 0;
            Task[] taskArray = null;
            string duration = null;

            DateTime start_time = DateTime.Now;

            fnUpdate_Status(string.Format("Starting iteration: {0} Records: {1}", iteration_count, processing_list.Count));
            Thread.Sleep(3000);

            foreach (Fake_PC my_pc in processing_list)
            {
                fake_pc_queue.Enqueue(my_pc);
            }

            Boolean execution_flag = true;

            int counter = 1;


            while (execution_flag == true)
            {

                // Task my_task = Task.Factory.StartNew(() => fnGet_Record_count());

                if (queue_count > 0)
                {
                    taskArray = new Task[number_of_threads];

                    for (int i = 0; i < number_of_threads; i++)
                    {

                            if (taskArray[i] == null || taskArray[i].Status == TaskStatus.RanToCompletion)
                            {
                                taskArray[i] = Task.Factory.StartNew(() => { return fnCreate3(fake_pc_queue, fake_pc_results, pc_error_list); });
                            }

                    }


                }
                else
                {
                    fnUpdate_Status("Waiting on threads to finish.");
                    Thread.Sleep(2000);
                }

                Task.WaitAny(taskArray);

            while (fake_pc_queue.IsEmpty == false)
            {
                Fake_PC my_pc;

                if (fake_pc_queue.TryDequeue(out my_pc))
                {
                    pc_error_list.Add(my_pc);
                }
            }

            fnUpdate_Task_Status("");
        }

Open in new window

0
 
Snarf0001Connect With a Mentor Commented:
Are you actually calling the same code for each object?  Obviously just sample data you provided.

If so, any reason not to just use Parallel.For (or .ForEach)?

You don't have as much lower level control, but does a very good job (in my experience) managing the different threads and tasks and splitting them out as needed across processors.

And is dirt simple to implement, with far less code.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.