Проблема многопоточности
Я перебираю строки в файле в каждой считаю кол-во букв "а", делаю это асинхронно и многопоточно. Почему в многопоточном методе кол-во букв "а" меньше, чем в асинхронном или в просто последовательном. То есть если перебрать строки и подсчитать буквы а, то последовательно будет 65913, асинхронно 65913, а многопоточно будет меньше, например 65909. Может быть какой-то поток мешает завершиться другому потоку? В чём проблема и как её решить? АСИНХРОННО
public static async Task<int> ReadFileAsync(string path)
{
int count = 0;
using (StreamReader sr = File.OpenText(path))
{
string line = String.Empty;
int i = 0;
while ((line = await sr.ReadLineAsync()) != null)
{
if (!String.IsNullOrEmpty(line))
{
i++;
MatchCollection matches3 = Regex.Matches(line, "а");
count += matches3.Count;
Console.WriteLine($"in line num {i} count symbol a equal: {matches3.Count}");
}
}
}
return count;
}
МНОГОПОТОЧНО
public static void ReadFileThread(string path)
{
StreamReader sr = File.OpenText(path);
string line = String.Empty;
while ((line = sr.ReadLine()) != null)
{
if (!String.IsNullOrEmpty(line))
{
Thread thread = new Thread(new ParameterizedThreadStart(WorkWithLines));
thread.Start(line);
}
}
}
private static void WorkWithLines(object line)
{
MatchCollection matches3 = Regex.Matches(line.ToString(), "а");
Console.WriteLine($"in line num count symbol a equal: {matches3.Count}");
CountForThirdMethod += matches3.Count;
}
Ответы (2 шт):
CountForThirdMethod += matches3.Count;
+= -не атомарная операция. Она разбивается на две
var newCountForThirdMethod = CountForThirdMethod + matches3.Count;
CountForThirdMethod = newCountForThirdMethod;
При выполнении в несколько потоков может быть наложение вида
// CountForThirdMethod == 0
Thread1: var newCountForThirdMethod = CountForThirdMethod + matches3.Count; // 1
Thread2: var newCountForThirdMethod = CountForThirdMethod + matches3.Count; // 1
Thread1: CountForThirdMethod = newCountForThirdMethod; // 1
Thread2: CountForThirdMethod = newCountForThirdMethod; // 1
т.е. вроде оба потока сделали +=, но один из результатов потерялся. Огораживайте эту строчку lock-ом, или используйте Interlocked.Add
Но вообще вам вроде многопоточнось не нужна, скорее всего узким местом все равно будет чтение из файла.
Thread thread = new Thread(new ParameterizedThreadStart(WorkWithLines));
thread.Start(line);
Это, конечно, прекрасно, но ваша программа может завершиться раньше, чем завершатся все запущенные таким образом потоки. Правильный шаблон работы с потоками - вы должны собрать собранные потоки в список, а потом сделать для всех запущенных потоков присоединение к основному потоку после их окончания:
thread.Join()
Скорее всего в вашем случае почти все потоки успевают завершиться успешно, кроме нескольких самых последних, судя по статистике. Вернее, вы успеваете где-то напечатать значение переменной CountForThirdMethod раньше, чем завершился последний поток (или несколько последних потоков).
Ну то есть примерно так:
var threads = new List<Thread>();
while ...
{
...
Thread thread = new Thread(new ParameterizedThreadStart(WorkWithLines));
thread.Start(line);
threads.Append(thread);
...
}
foreach thread in threads
{
thread.Join();
}