0

I have a program that generates txt files with randomized contents. Many documents have to be generated, so I use Tasks to get the work done faster. I want to notify the user of the progress that's being made every few seconds (ex: "Generated 50000 documents out of 100000"). I create another Task, RecordProgress(), which records progress every 3 seconds.

However, if the program generates many tasks, the RecordProgress() never runs. If the program only generates 4 tasks, then RecordProgress() runs correctly ie. gets called every 3 seconds. However, if the program generates many tasks, RecordProgress() only runs once processing is/close to being finished.

Is there any way to increase the priority of the RecordProgress() task?

I've tried logging progress in each task, but that generates too many log messages to the console, which severely slows down my program.

I've tried logging in each task and waiting 3 seconds between logs, but if the program generates 50 tasks, then 50 messages will be logged to the console at the same time, which once again slows down my program and is unnecessary. I'd rather have ONE log message to the console every 3 seconds.

public void RecordProgress() 
{
        Stopwatch sw = new Stopwatch();
        sw.Start();

        //only record data while this generator is generating
        while (_processing)
        {
            if(sw.ElapsedMilliseconds < _logFrequency)
                continue;

            Console.WriteLine("Generated " + _docsGenerated + " documents.");
            sw.Restart();
        }
}

public void GenerateDocs()
{
    List<Task> tasks = new List<Task>();

    _processing = true;
    for (i = 0; i < 50; i ++)
    {
        tasks.Add(Task.Run(() => DoWork());
    }

    //task which records progress
    //ONLY runs if not many tasks are created above
    Task.Run(() => RecordProgress());

    Task.WaitAll(tasks.ToArray());
}

I'd like the RecordProgress() task to run every 3 seconds regardless of the number of tasks generated by this program.

Edit: As per the comments, I removed the use of Thread.Sleep(). However, that only delayed the starting of my RecordProgress() task.

I've attempted to use a Stopwatch in RecordProgress() to only record progress every 3 seconds, but it greatly slows the performance of my program.

So new question: how to record progress of tasks without using a timer that heavily impacts performance?

V1aD1
  • 3
  • 3
  • 4
    Do not use `Thread.Sleep` in the TPL, use `Task.Delay`. With the TPL it is possible that you are not even running on threads, depending on your scheduler. – Sefe Jan 09 '19 at 10:18
  • See https://stackoverflow.com/questions/46491445/how-to-ensure-a-new-thread-is-created-with-task-run – NineBerry Jan 09 '19 at 10:22
  • @Sefe Thread.Sleep delays the completion of the thread, so once I swapped to using Task.Delay, it would delay the start of my RecordProgress(), but then the task would continue outputting to the console, without any delay between logs, thereby slowing down my program. I tried using a Stopwatch in RecordProgram(), but it greatly slows down the performance of my program. – V1aD1 Jan 09 '19 at 11:54
  • The default task scheduler is not equipped to do something like this. If you need code to run at specific intervals, use a timer (`System.Threading.Timer`). You can have the tasks invoke a (thread-safe!) method on an object to collect messages, or have the logging method itself query progress. – Jeroen Mostert Jan 09 '19 at 12:12
  • @JeroenMostert thanks, your comments led me to a solution, I now understand that tasks cannot have different priorities. – V1aD1 Jan 10 '19 at 08:19

2 Answers2

0

In the original case, you create many tasks and exhaust the available threads in the Thread Pool. Running ReportProgress last delays its execution until most of the other tasks are complete. I see you corrected you code.

As for the priority question. Have in mind that you cannot change the priority of a task. You may achieve something like it by implementing your own TaskScheduler, but by default all tasks run a normal priority.

If you really need to run a task in higher priority, you need to create a Thread and set its priority to higher / highest. Something you should be very careful about.

Nick
  • 4,787
  • 2
  • 18
  • 24
-2

I've found it:

I've created a Stopwatch object that requires the use of a lock to access. At the end of my DoWork() task, the task locks and checks how much time has passed since the program last logged. If over 3 seconds have passed, the task logs progress and resets the Stopwatch object. The RecordProgress() task is no longer necessary.

public void GenerateDocs()
{
    List<Task> tasks = new List<Task>();

    lock (_lockForLog)
    {
        _swForLogging.Start();
    }
    _processing = true;
    for (i = 0; i < 50; i ++)
    {
        tasks.Add(Task.Run(() => DoWork());
    }

    Task.WaitAll(tasks.ToArray());

    lock (_lockForLog)
    {
        _swForLogging.Stop();
    }
}

public void DoWork(){
    //do work

    lock (_lockForLog)
    {
        if (_swForLogging.ElapsedMilliseconds > 3000)
        {
            Console.WriteLine("Generated " + _docsGenerated + " documents.");
            _swForLogging.Restart();
        }
    }
}
V1aD1
  • 3
  • 3