Я чувствую, что заново изобретаю колесо. Отправка работы в конкретную ветку. Я хочу обрабатывать события из нескольких потоков последовательно в одном потоке - PullRequest
0 голосов
/ 05 ноября 2018

Я знаю, что подобные вещи существуют в WPF и формируют приложения с помощью метода Control.Invoke, я также знаю о существовании BackgroundWorker, ThreadPool и т. Д.

Однако я не хочу зависеть от Forms / WPF и хочу убедиться, что работа выполняется последовательно и в одном потоке.

Редактировать: Обоснование: я хочу управлять конечным автоматом из одного потока. События приходят из других жестких тем. Там нет пользовательского интерфейса.

Пока я не мог понять, как это сделать с существующими классами фреймворка, но я мог неправильно понять документацию.

Редактировать: я забыл упомянуть, что я связан с .NET Framework 3.5

То, что я написал до сих пор:

public class Dispatcher
{
    string Name;
    Thread WorkerThread;
    Queue<Action> WorkQueue;
    List<Exception> Exceptions;
    ManualResetEvent Gate;
    volatile bool KeepRunning;
    readonly object WorkLocker;

    public override string ToString()
    {
        return String.Format("{0}({1})", this.GetType().Name, Name);
    }

    public Dispatcher(string name)
    {
        Name = name;
        WorkLocker = new Object();
        Gate = new ManualResetEvent(false);
        WorkQueue = new Queue<Action>();
        Exceptions = new List<Exception>();
    }

    public void Start()
    {
        if (WorkerThread == null)
        {
            WorkerThread = new Thread(doDispatch)
            {
                IsBackground = true,
                Name = this.Name
            };
            WorkerThread.Start();
        }
    }

    public void Stop()
    {
        if (WorkerThread != null && WorkerThread.IsAlive)
        {
            Dispatch(() => { KeepRunning = false; });
            WorkerThread.Join();
        }
        WorkerThread = null;
    }

    public void Reset()
    {
        Stop();
        lock (WorkLocker)
        {
            WorkQueue = new Queue<Action>();
            Exceptions = new List<Exception>();
        }
    }

    public void Dispatch(Action a)
    {
        lock (WorkLocker)
        {
            WorkQueue.Enqueue(a);
        }
        Gate.Set();
    }

    public List<Exception> CollectExceptions()
    {
        List<Exception> result = new List<Exception>();
        lock(WorkLocker)
        {
            foreach(Exception e in Exceptions)
            {
                result.Add(e);
            }
            Exceptions.Clear();
        }
        return result;
    }

    private void doDispatch()
    {
        KeepRunning = true;
        while (KeepRunning)
        {
            Gate.WaitOne();
            lock (WorkLocker)
            {
                while (WorkQueue.Count > 0)
                {
                    try
                    {
                        WorkQueue.Dequeue()?.Invoke();
                    }
                    catch (Exception e)
                    {
                        Exceptions.Add(e);
                    }
                }
            }
        }
    }
}

Есть ли способ сделать что-то подобное более простым способом? Еще одна полезная функция - возможность отправлять вызовы с несколькими аргументами.

1 Ответ

0 голосов
/ 05 ноября 2018

Поскольку вы связаны с 3.5, вы не можете использовать BlockingCollection или библиотеку DataFlow ... вам придется развернуть свою собственную реализацию.

Пример кода, который вы предоставили, является хорошим началом, но вы должны применить принцип единой ответственности, чтобы сделать его более понятным и простым для рефакторинга, когда (если?) Вы обновляете .NET Framework.

Я бы сделал это так:

  • Создать потокобезопасный класс-оболочку вокруг Queue, который несколько mimics BlockingCollection, этот ответ дает хороший пример
  • Структурируйте свой код вокруг потока потребителя / производителя и добавьте оболочку
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...