Threading - гарантия получения последнего результата - PullRequest
1 голос
/ 31 января 2020

У меня есть несколько потоков, которые вызывают метод. Это выглядит примерно так:

public void DoWork(params int[] initialConditions)
{
    //Do a lot of work
}

Однако, если условия быстро меняются, я получаю много устаревших промежуточных результатов, потому что не могу достаточно быстро завершить вычисления. Я знаю! Я изменю код так, чтобы он выглядел так:

public void DoWork(params int[] initialConditions)
{
    if(Monitor.TryEnter(syncLock)
    {
        //Do a lot of work
        Monitor.Exit(syncLock);
    }
}

Теперь я не буду потрудиться делать вычисления, пока мои предыдущие вычисления не будут закончены. Я буду немного отставать в быстро меняющихся ситуациях, но не больше, чем был бы, и я не буду тратить свое время на выполнение всей этой дополнительной работы для устаревших результатов.

Однако,

Как только ситуация перестает меняться, я все еще немного устарел, и последний поток, который хотел вызвать DoWork, давно ушел. Есть ли способ сообщить нить:

if no one is doing work
    do work
else
    wait to do work until after the other thread finishes
but
    if a new thread arrives before you start doing work, leave without doing work.

1 Ответ

0 голосов
/ 06 февраля 2020

Этот код должен соответствовать вашим требованиям, описанным псевдокодом:

class Program
{
    static object _workLockObject = new object();
    static volatile int _currentPriority = 0;

    static void Main()
    {
        Task t1 = new Task(() => TryDoWork(1));
        Task t2 = new Task(() => TryDoWork(2));
        Task t3 = new Task(() => TryDoWork(3));

        t1.Start();
        Thread.Sleep(100);
        t2.Start();
        Thread.Sleep(100);
        t3.Start();
        Console.ReadKey();
    }
    public static void TryDoWork(params int[] initialConditions)
    {
        var priotity = Interlocked.Increment(ref _currentPriority);
        while (!Monitor.TryEnter(_workLockObject))// starting to wait when DoWork is available
        {
            if (priotity != _currentPriority) // if the thread has stale parameters
            {
                Console.WriteLine("DoWork skipped " + initialConditions[0]);
                return;// skipping Dowork
            }
            Thread.Sleep(300);// Change the interval according to your needs
        }
        try // beginning of critical section
        {
            if (priotity == _currentPriority) // if the thread has the newest parameters
                DoWork(initialConditions);
        }
        finally
        {
            Monitor.Exit(_workLockObject); // end of critical section
        }
    }
    public static void DoWork(params int[] initialConditions)
    {
        Console.WriteLine("DoWork started " + initialConditions[0]);
        Thread.Sleep(5000);
        Console.WriteLine("DoWork ended " + initialConditions[0]);
    }
}

Interlocked.Increment гарантирует, что каждый поток имеет свой собственный приоритет, а последний поток имеет приоритет, который позволяет выполнять DoWork, когда он есть в наличии.

...