Ciao a tutti
Voglio postare un esempio di multithreading in cui leggo e comprimo tutti i file di una directory (e sottodirectory).
L'approccio è questo:
1) Leggiamo tutti i nomi dei file e li mettiamo in una coda
2) Creiamo dei thread in cui cicliamo sulla coda fino all'esaurimento
3) In ogni thread mettiamo un Handle con cui segnalare al thread principale la fine dell'esecuzione
4) Alla fine serializziamo il bag che rappresenta il file di output
Vediamo come fare in una applicazione c# console di esempio:
[Serializable] //questa è la classe di scambio
public class DataClass
{
public string FName { get; set; }
public byte[] FData { get; set; }
}
static void Main(string[] args)
{
string inputPath = @"C:\Users\Antonio\Desktop\PRC";
//mi creo una coda FIFO per accedere all'elenco dei nomi dei files in modo threadsafe dai threads che andrò a creare
var fileNames = new Queue<string>(Directory.GetFiles(inputPath, "*.*", SearchOption.AllDirectories));
var locks = new List<AutoResetEvent>(); //la lista di Handle
var data = new List<DataClass>(); //la bag di output
var st = new Stopwatch(); //per testare il tempo
var threadCount = Environment.ProcessorCount * 2; //il numero di thread, in questo caso è variabile in base alla cpu della macchina
st.Start();
Console.WriteLine("Threads: {0}", threadCount);
for (int i = 1; i < threadCount; i++) //itero sul numero di thread che ho deciso di creare
{
var t = new Thread(delegate(object l) //essendo un metodo anonimo, questo è quello che verrà eseguito all'interno del thread
{
while (fileNames.Count>0)
{
string fname;
//scodo un nome di file
lock (fileNames)
fname = fileNames.Dequeue();
//leggo e comprimo il file
using (var m = new MemoryStream())
{
using (var df = new DeflateStream(m, CompressionMode.Compress))
{
var b = File.ReadAllBytes(fname);
df.Write(b, 0, b.Length);
}
//aggiungo il file compresso nella mia variabile di output
data.Add(new DataClass() { FName = fname, FData = m.ToArray() });
}
}
(l as AutoResetEvent).Set(); //segnalo che questo thread ha completato il suo lavoro
});
//creo un nuovo Handle da usare come flag di fine elaborazione
var k = new AutoResetEvent(false);
locks.Add(k);
t.Start(k);
}
//aspetto che tutti i thread abbiano finito di elaborare
AutoResetEvent.WaitAll(locks.ToArray());
st.Stop();
Console.WriteLine("OK in {0}s", st.Elapsed.Seconds);
st.Reset();
st.Start();
//scrivo in output il file
using (var w = File.OpenWrite(@"output.dat"))
{
var b = new BinaryFormatter();
b.Serialize(w, data);
}
st.Stop();
Console.WriteLine("OK in {0}s", st.Elapsed.Seconds);
Console.ReadLine();