Последовательное выполнение


Я сделал класс, который я думаю, удовлетворяет условиям:

  1. Actionмогут быть добавлены в очередь в любое время, любое количество клиентов.

  2. Action'ы выполняются в том порядке, в котором были добавлены.

  3. Только один Action выполнение на время.

public sealed class SequentialTaskScheduler
{
    private static volatile SequentialTaskScheduler instance = null;

    private static readonly object padlock = new object();

    private readonly ConcurrentQueue<Action> queue = new ConcurrentQueue<Action>();

    SequentialTaskScheduler()
    {
        var task = Task.Factory.StartNew(ThreadProc);
    }

    public static SequentialTaskScheduler Instance
    {
        get
        {
            if (instance == null)
            {
                lock (padlock)
                {
                    if (instance == null)
                    {
                        instance = new SequentialTaskScheduler();
                    }
                }
            }
            return instance;
        }
    }

    public void AddToQueueForExecution(Action action)
    {
        queue.Enqueue(action);
    }

    private void ThreadProc()
    {
        while (true)
        {
            Action item;
            bool isSuccessfull = false;
            isSuccessfull = queue.TryDequeue(out item);
            if (isSuccessfull)
            {
                item();
            }
            System.Threading.Thread.Sleep(100);
        }
    }
}

Я буду благодарен за советы о вреде такой реализации. А также будем признательны за советы, как улучшить его.

Обновление:

У меня получился отзыв на этот задание от компании.

  • неопределенной использования летучих

  • использовать многопотоковое исполнение синглтон или синглтон

  • Задач.Фабрика.StartNew взять бассейн с потоком, но не возвращает его.

  • используйте занят петля с резьбой.Сон-это не элегантное решение.

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



Комментарии
2 ответа

У меня есть несколько предложений, и модифицированный код.


  1. Используйте ленивый паттерн Singleton вместо того, чтобы проверить синглтон, избегайте использования летучих

    private static readonly Lazy<SequentialTaskExecutor> _lazy = new 
    Lazy<SequentialTaskExecutor>(() => new SequentialTaskExecutor());

  2. Задачи должны быть отменены и правильно утилизировать. Поскольку задача мы хотим запустить на долгое время, создать новую задачу, используя задач.Фабрика.StartNew с токены отмены и TaskCreationOptions.Затяжные

    Task.Factory.StartNew(this.ExecuteTask, _token, TaskCreationOptions.LongRunning, TaskScheduler.Default);

  3. Класс должен реализовывать интерфейс IDisposable, так что созданные потоки могут быть корректно отменены и детали в очереди будут удалены

    public class SequentialTaskExecutor : IDisposable

    protected virtual void Dispose(bool disposing)
    {
    try
    {
    if (!disposedValue)
    {
    if (disposing)
    {
    // clear the items in queue.
    Action item;
    while (_queue.Count != 0)
    {
    if (_queue.TryDequeue(out item))
    {
    }
    }

    _tokenSource.Cancel();
    _executionTask.Wait();
    _tokenSource.Dispose();
    _executionTask.Dispose();
    }

    disposedValue = true;
    }
    }
    catch
    {
    }
    finally
    {
    _tokenSource = null;
    _executionTask = null;
    }
    }


  4. Использовать смарт вместо напряженного ожидания в задачи.

    private void ExecuteTask()
    {
    bool isTaskCompleted = true;
    while (true)
    {
    Action item;
    if (_token.IsCancellationRequested)
    {
    while (_queue.Count != 0)
    {
    if (_queue.TryDequeue(out item))
    {
    }
    }

    break;
    }

    if (isTaskCompleted)
    {
    isTaskCompleted = false;
    var innerTask = Task.Factory.StartNew(() =>
    {
    if(_token.IsCancellationRequested)
    {
    return;
    }
    if(_queue.TryDequeue(out item))
    {
    item();
    }

    }, _token).ContinueWith((ants) =>
    {
    if(ants.IsCanceled || ants.IsFaulted)
    {
    isTaskCompleted = true;
    }
    else if(ants.IsCompleted)
    {
    isTaskCompleted = true;
    }
    _currentInterval = _minimumInterval;
    System.Diagnostics.Debug.WriteLine(Task.CurrentId + " " + DateTime.Now.ToString() + " Task execution complted _currentInterval : " + _currentInterval);
    });
    }
    else
    {
    // Raise the interval till maximum value
    if (_currentInterval < _maximumInterval)
    {
    _currentInterval++;
    }

    System.Diagnostics.Debug.WriteLine(Task.CurrentId + " " + DateTime.Now.ToString() + " Task waiting complted _currentInterval : " + _currentInterval);
    Thread.Sleep(TimeSpan.FromSeconds(_currentInterval));
    }
    }
    }


  5. Полный исходный код выглядит следующим образом

    public class SequentialTaskExecutor : IDisposable
    {
    private static readonly Lazy<SequentialTaskExecutor> _lazy = new Lazy<SequentialTaskExecutor>(() => new SequentialTaskExecutor());
    private readonly ConcurrentQueue<Action> _queue = null;
    private CancellationTokenSource _tokenSource = null;
    private CancellationToken _token;
    private int _maximumInterval = 10;
    private int _minimumInterval = 1;
    private int _currentInterval = 0;
    private bool disposedValue = false;
    Task _executionTask = null;

    private SequentialTaskExecutor()
    {
    _queue = new ConcurrentQueue<Action>();
    _tokenSource = new CancellationTokenSource();
    _token = _tokenSource.Token;
    _executionTask = Task.Factory.StartNew(this.ExecuteTask, _token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
    }

    public static SequentialTaskExecutor Instance
    {
    get
    {
    return _lazy.Value;
    }
    }

    public void AddToQueueForExecution(Action action)
    {
    _queue.Enqueue(action);
    System.Diagnostics.Debug.WriteLine("Task added " + _queue.Count);
    }

    private void ExecuteTask()
    {
    bool isTaskCompleted = true;
    while (true)
    {
    Action item;
    if (_token.IsCancellationRequested)
    {
    while (_queue.Count != 0)
    {
    if (_queue.TryDequeue(out item))
    {
    }
    }

    break;
    }

    if (isTaskCompleted)
    {
    isTaskCompleted = false;
    var innerTask = Task.Factory.StartNew(() =>
    {
    if(_token.IsCancellationRequested)
    {
    return;
    }
    if(_queue.TryDequeue(out item))
    {
    item();
    }

    }, _token).ContinueWith((ants) =>
    {
    if(ants.IsCanceled || ants.IsFaulted)
    {
    isTaskCompleted = true;
    }
    else if(ants.IsCompleted)
    {
    isTaskCompleted = true;
    }
    _currentInterval = _minimumInterval;
    System.Diagnostics.Debug.WriteLine(Task.CurrentId + " " + DateTime.Now.ToString() + " Task execution complted _currentInterval : " + _currentInterval);
    });
    }
    else
    {
    // Raise the interval till maximum value
    if (_currentInterval < _maximumInterval)
    {
    _currentInterval++;
    }

    System.Diagnostics.Debug.WriteLine(Task.CurrentId + " " + DateTime.Now.ToString() + " Task waiting complted _currentInterval : " + _currentInterval);
    Thread.Sleep(TimeSpan.FromSeconds(_currentInterval));
    }
    }
    }

    protected virtual void Dispose(bool disposing)
    {
    try
    {
    if (!disposedValue)
    {
    if (disposing)
    {
    // clear the items in queue.
    Action item;
    while (_queue.Count != 0)
    {
    if (_queue.TryDequeue(out item))
    {
    }
    }

    _tokenSource.Cancel();
    _executionTask.Wait();
    _tokenSource.Dispose();
    _executionTask.Dispose();
    }

    disposedValue = true;
    }
    }
    catch
    {
    }
    finally
    {
    _queue = null;
    _tokenSource = null;
    _executionTask = null;
    }
    }

    public void Dispose()
    {
    Dispose(true);
    }
    }
    }


3
ответ дан 9 апреля 2018 в 08:04 Источник Поделиться

Я в основном согласен с Вишну вардханом'ы ответ. Однако я не согласен с его 4-е предложение, которое я думаю, вряд ли лучше.

Он по-прежнему использует Thread.Sleep которые никогда не должны использоваться в производственном коде: вы не должны платить своему работнику, чтобы спать. Либо использовать BlockingCollection или написать собственную синхронизацию через ManualResetEvent. Если нет задачи процессов поток должен просто повесить на немного переждать ручки. Он не должен крутиться в цикле, постоянно потребляя ресурсы процессора, а не значимая работа.

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

П. С. распространенная ошибка при использовании задач.Фабрика.

1
ответ дан 9 апреля 2018 в 12:04 Источник Поделиться