Desenvolupant l'aplicació que verifica el bitrotting dels fitxers m'he trobat en la necessitat d'iniciar diferents threads, un thread per cada disc dur
En total al servidor hi ha 8 discos, 4+4, cal verificar un fitxer i la seva còpia contra el seu hash guardat a la BBDD. Això vol dir 8 threads que fan el mateix, calcular hashs de fitxers. Enlloc d'escriure el mateix 8 cops vaig pensar de fer un bucle per iniciar-los.
Bé? Malament. Els threads són molt tisquimiquis amb les variables que els inicien i el seu valor, ja que si són d'un context comú aleshores es trepitgen. Però millor un exemple, primer un codi que no funciona:
int i = 0;
clsVerificador Verificadors = null;
foreach (DataRow DR in TBL.Rows)
{
Verificadors = new clsVerificador(this, DR.Field<int>("IdUnitat"), DR["Path"].ToString(), i);
threadsVerificadors[i] = new Thread(delegate() { Verificadors.VerificaHashes(); });
threadsVerificadors[i].Start();
i++;
Verificadors = new clsVerificador(this, DR.Field<int>("IdUnitat"), DR["PathBackup"].ToString(), i);
threadsVerificadors[i] = new Thread(delegate() { Verificadors.VerificaHashBackup(); });
threadsVerificadors[i].Start();
i++;
}
Sembla correcte, i de fet si s'executa només amb un thread funciona bé. Però no. Resulta que quan el thread comença a executar-se han passat uns nanosegons des de que va deixar el codi principal, i agafa els valors de les variables en aquell moment. Com el bucle va més ràpid que la creació dels threads aleshores tots els threads tenen el valor de les variables de la última volta del bucle. Quina és la solució? Obligar al compilador a crear noves variables a cada volta del bucle. Així:
Aquest seria el codi que funciona:
clsVerificador[] Verificadors = new clsVerificador[8];
foreach(DataRow DR in TBL.Rows)
{
int j = i;
Verificadors[j] = new clsVerificador(this, DR.Field<int>("IdUnitat"), DR["Path"].ToString(), j);
threadsVerificadors[j] = new Thread(delegate() { Verificadors[j].VerificaHashes(); });
threadsVerificadors[j].Start();
i++;
int k = i;
Verificadors[k] = new clsVerificador(this, DR.Field<int>("IdUnitat"), DR["PathBackup"].ToString(), k);
threadsVerificadors[k] = new Thread(delegate() { Verificadors[k].VerificaHashBackup(); });
threadsVerificadors[k].Start();
i++;
}
Es declaren dins del bucle variables, i així són noves. També cal fer que la variable que el "new" sigui nova cada cop. Es pot fer declarant-la dins del bucle o fent-la un array. Funciona! |