Реализация прогресса выполнения задачи с отдельных потоков - C#
Формулировка задачи:
Всем здравствуйте!
Опишу более подробно свою проблему, надеюсь большое количества текста не оттолкнёт.
Во многих своих приложениях я использую сканер локальной сети, некий самописный класс с подклассами, который за максимально короткое время должен выдать мне информацию о активных хостах в сети с максимальной точностью.
Для реализации я решил создать n-ное количество потоков (в основном для сканирования подсети с маской 24, а это 254 потока), записать их в некий список List и после этого запустить потоки на выполнение. В потоках, как нетрудно догадаться, проводится пинг хостов с определенными параметрами (вроде количества попыток, таймаута и т.д.)
И вот в чём проблема - мне нужно отслеживать некое события, назовём его Scan_Progress_Changed, которое вызывается каждый раз, когда прогресс сканирования изменяется хотя-бы на 1%. И по сути прогресс меняется при завершении каждого с потоков. Пока что я реализовал это таким образом: после запуска потоков на выполнение, в цикле foreach для каждого из потоков выполняется Join, после каждого с которых, соответственно, изменяется значение прогресса сканирования. Только вот цикл перебирает потоки последовательно, в то время как завершаться они могут в хаотичном порядке. Как результат - прогресс отображается рывками и ни о какой плавности и информативности речи быть не может.
Я пытался вызывать событие прямо под конец каждого с выполняемых потоков, но это не приводило ни к чему хорошему - мало того, что событие вызывалось не с основного потока (а этого бы хотелось), так еще и при попытке прикрутить ко всему этому какой-нибудь прогресс-бар, изменяя его значение через Invoke, весь этот каскад потоков зависал.
Направьте, пожалуйста, в правильное русло. Спасибо.
Решение задачи: «Реализация прогресса выполнения задачи с отдельных потоков»
textual
Листинг программы
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
using System.Net.NetworkInformation;
namespace WindowsFormsApplication1 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
button1.Click += new EventHandler(button1_Click);
}
void button1_Click(object sender, EventArgs e) {
string[] addresses ={"50.18.212.157","50.18.212.223","107.23.48.232",
"107.23.48.182","54.68.183.151","54.68.165.206",
"54.67.52.245","54.67.48.128", "54.187.208.163"
};
progressBar1.Value = 0;
progressBar1.Maximum = addresses.Length;
listBox1.Items.Clear();
Scanner scanner = new Scanner();
scanner.ScannerEvent += new EventHandler<ScannerEventArgs>(scanner_ScannerEvent);
scanner.ScanParallel(addresses);
}
void scanner_ScannerEvent(object sender, ScannerEventArgs e) {
listBox1.Invoke((Action)(() => {
string result = string.Format("Address: {0}, Status: {1}", e.Address, e.Status);
listBox1.Items.Add(result);
}));
progressBar1.Invoke((Action)(() => {
progressBar1.Value++;
}));
}
}
public class Scanner {
public event EventHandler<ScannerEventArgs> ScannerEvent;
public void ScanParallel(string[] addresses) {
Task.Factory.StartNew(() => {
ParallelOptions options = new ParallelOptions() {
MaxDegreeOfParallelism = addresses.Length
};
Parallel.ForEach(addresses, options, address => {
Ping ping = new Ping();
PingReply reply = ping.Send(address);
OnScannerEvent(new ScannerEventArgs(reply.Address, reply.Status));
});
});
}
protected virtual void OnScannerEvent(ScannerEventArgs e) {
if (ScannerEvent != null) {
ScannerEvent(this, e);
}
}
}
public class ScannerEventArgs : EventArgs {
public readonly IPAddress Address;
public readonly IPStatus Status;
public ScannerEventArgs(IPAddress address, IPStatus status) {
Address = address;
Status = status;
}
}
}