Question

My program needs to constantly perform many repetitive calculations as fast as possible. There are many tasks running parallelly which cause CPU utilisation is at 100%. To let users slow down processing overload(a little under 100% of CPU, depending on hardware), I added

await Task.Delay(TimeSpan.FromMilliseconds(doubleProcessingCycleIntervalMilliseconds));

to heavy processing methods. This works perfect as far as value of doubleProcessingCycleIntervalMilliseconds is at least 1 ms. For users who have high-end computers(calculations speed will take less than one millisecond), I wanted to add same option for delay but instead of milliseconds using ticks. So now code looks:

if (ProcessingCycleIntervalOptionsMilliseconds == true)
{ 
   await Task.Delay(TimeSpan.FromMilliseconds(doubleProcessingCycleIntervalMilliseconds)); 
}
else
{ 
   await Task.Delay(TimeSpan.FromTicks(longProcessingCycleIntervalTicks)); 
}

When walue of longProcessingCycleIntervalTicks is at least 10000 ticks(=1ms) program works perfect. Unfortunately when values go under 1ms(0 for doubleProcessingCycleIntervalMilliseconds which I can understand) or under 10000(i.e. 9999 for longProcessingCycleIntervalTicks) program becomes not responsive. So literally difference of 1 tick below 1ms hangs the program. I don't use MVVM. (Just in case: I checked Stopwatch.IsHighResolution gives true on the development computer)

Is it possible/correct to use

await Task.Delay(TimeSpan.FromTicks(longProcessingCycleIntervalTicks)); 

in .NET 4.5.1 ? If yes, then how to determine when user can use it?

Était-ce utile?

La solution 2

You might be better off looking at the way you are performing the tasks rather than trying to sleep them.

The best way I can think of is by using a task manager to manage each task independently (such as a background worker) and then thread collections of tasks.

This would enable you to manage how many tasks are running instead of trying to 'slow' them down..

i.e

public class Task<returnType>
{
    public delegate returnType funcTask(params object[] args);
    public delegate void returnCallback(returnType ret);

    public funcTask myTask;
    public event returnCallback Callback;

    public Task(funcTask myTask, returnCallback Callback)
    {
        this.myTask = myTask;
        this.Callback = Callback;
    }

    public void DoWork(params object[] args)
    {
        if (this.Callback != null)
        {
            this.Callback(myTask(args));
        }
        else
        {
            throw new Exception("no Callback!");
        }
    }
}

Then you need a manager that has a Queue in it of the tasks you want to complete, call myQueue.Enqueue to queue, myQueue.Dequeue to run the tasks. Basically you can use the already built-in Queue to do this.

You then can create a Queue of task managers full of tasks and have them all run asychronously, and stack nicely on the CPU as they are event driven and the OS and .NET will sort out the rest.

EDIT: To continuously run tasks you will need to create a class that inherits the Queue class, then call an event when something is de-queued. The reasoning behind why I say to use events is that they stack on the CPU.

For a neverending stackable 'Loop' something like this would work...

public class TaskManager<T> : Queue<T>
{

    public delegate void taskDequeued();

    public event taskDequeued OnTaskDequeued;

    public override T Dequeue()
    {
        T ret = base.Dequeue();
        if (OnTaskDequeued != null) OnTaskDequeued();
        return ret;
    }

}

In your function that instantiates the 'loop' you need to do something like...

TaskManager<Task<int>> tasks = new TaskManager<Task<int>>();

Task<int> task = new Task<int>(i => 3 + 4, WriteIntToScreen); // WriteIntToScreen is a fake function to use as the callback

tasks.Enqueue(task);

tasks.OnTaskDequeued += delegate 
{ 
   tasks.Enqueue(task);
   tasks.Dequeue.Invoke();
};

// start the routine with
tasks.Dequeue.Invoke(); // you call do some async threading here with BeginInvoke or something but I am not gonna write all that out as it will be pages...

To cancel you just empty the queue.

Autres conseils

Your intention is not to keep CPU utilization below 100%. Your intention is to keep the system responsive. Limiting CPU utilization is a misguided goal.

The way you do this is by using low priority threads. Use a custom task scheduler for your CPU bound tasks.

Timing in Windows has limited accuracy. Thread.Sleep cannot work with fractional milliseconds. .NET rounds them away before handing over to Sleep.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top