Изучаю многопоточность. Столкнулся со следующей проблемой. Вот такой код:
public class WORK{
private int number;
public Action<int> delegWork; // Обобщ.делегат
public void Working(int num){
this.number += num; // Какая-то полезная работа с результатов переданным в this.number
ThreadPool.QueueUserWorkItem((object n) => { this.delegWork((int)n); }, this.number ); //Запуск делегата
}
}
public class ANALYSIS{
private readonly Form _form; // Windows Form
public Action<int> delegAnalysis; // Обобщ.делегат
public ANALYSIS(Form form){
this._form = form;
this.Make();
}
// Метод класса, в котором происходит присвоение делегату анон.метода
private void Make(){
this.delegAnalysis += (number) => {
this._form.Invoke((MethodInvoker)delegate{
Thread.Sleep(25); // Эмуляция полезной работы
Console.WriteLine ("Number: " + number);
});
};
}
}
.....
/* В основном потоке */
WORK worker = new WORK();
ANALYSIS analyzer = new ANALYSIS(this); // В конструктор передаем текущий Windows Form приложения
worker.delegWork += (number) => { analyzer.delegAnalysis(number); };
//Цикл в основном потоке (например построчное чтение из файла и т.п.)
for(int i = 0; i < 500; i++){
worker.Working(i);
}
Описание кода — в методе Working класса WORK делается какая-то полезная работа, далее результат этой работы посредством делегатов delegWork и delegAnalysis отправляется в анон.метод класса ANALYSIS в котором производится анализ результатов работы класса WORK. Соответсвенно сама работа может выполняться быстрее, чем ее анализ, но сам анализ должен идти строго поочередно друг за другом, а не хаотично. Т.е. следующий вызванный метод через delegAnalysis должен дождаться результатов работы предыдущего вызова delegAnalysis.
Многопоточность реализовал с помощью ThreadPool.QueueUserWorkItem т.к. он более экономичный. В данном случаем Console.WriteLine отрабатывает в каком-то отдельном потоке/потоках и в консоле я вижу примерно следующее:
1
3
6
4
2
5
...
500
489
499
Как можно заметить, цифры следуют
не по порядку.
Вопрос таков — как мне сделать поочередное исполнение потоков ? Чтобы следующий поток ожидал пока предыдущий не закончит свою работу. И желательно как можно менее ресурсоемко.
PS. На данный момент присматриваюсь к рекурсии и что-то вроде Task Continuewith (или await/join), но как собрать и обернуть все во едино сообразить не могут.
На данный момент решил таким образом:
С использованием BlockingCollection и запуском второго потока в конструкторе при инициализации класса WORK.
public class WORK{
private BlockingCollection<Func<Task>> _collection = new BlockingCollection<Func<Task>>();
private Thread ConsumerThread;
private int number;
public Action<int> delegWork; // Обобщ.делегат
//Конструктор
public WORK(){
this.ConsumerThread = new Thread(this.LaunchThread);
this.ConsumerThread.Start();
}
public void Working(int num){
this.number += num; // Какая-то полезная работа с результатов переданным в this.number
int index = this.number; this._collection.Add(new Func<Task>(async () => { this.delegWork(index); })); // Добавляем делегат во второй поток через BlockingCollection
}
// Метод для второго потока
private async void LaunchThread()
{
while (true)
{
var processTask = this._collection.Take();
await Task.Run(processTask);
}
}
}
public class ANALYSIS{
private readonly Form _form; // Windows Form
public Action<int> delegAnalysis; // Обобщ.делегат
public ANALYSIS(Form form){
this._form = form;
this.Make();
}
// Метод класса, в котором происходит присвоение делегату анон.метода
private void Make(){
this.delegAnalysis += (number) => {
this._form.Invoke((MethodInvoker)delegate{
Console.WriteLine ("Number in analyzer: " + number);
});
};
}
}
.....
/* В основном потоке */
WORK worker = new WORK();
ANALYSIS analyzer = new ANALYSIS(this); // В конструктор передаем текущий Windows Form приложения
//Подписываем делегат
worker.delegWork += (number) => { analyzer.delegAnalysis(number); };
worker.delegWork += (number) => { Console.WriteLine ("Number: " + number); };
//Второй поток для задачи (например построчное чтение из файла и т.п.)
new Thread(() =>
{
for(int i = 0; i < 500; i++){
worker.Working(i);
}
}).Start();