Почему «блокировка» блока не работает? - PullRequest
2 голосов
/ 08 сентября 2011
public class ThreadInteroperateWithLock
{
    private int m_count;
    private object m_synLock;
    public ThreadInteroperateWithLock() 
    {
        m_count = 0;
        m_synLock = new object();
    }

    public int Count { get { return m_count; } }

    public void Add() 
    {
        //just simulate some work
        int temp=0;
        for (int i = 0; i < 10000; i++)
        {
            temp++;
        }

        //really job
        lock (m_synLock)
        {
            m_count++;
        }
    }
}

Этот код находится в консольном приложении:

ThreadInteroperateWithLock ope = new ThreadInteroperateWithLock();
Thread[] threadArray = new Thread[100];
for (int i = 0; i < 100; i++)
{
    Thread thread = new Thread(new ThreadStart(ope.Add));
    thread.IsBackground = false;
    threadArray[i] = thread;
}
for (int i = 0; i < 100; i++)
{
    threadArray[i].Start();
}
Console.WriteLine(ope.Count);
Console.ReadKey();

Иногда он печатает «99», а иногда «100», независимо от того, существует блок lock{...} или нет.Что-то не так в моем коде?

Ответы [ 7 ]

4 голосов
/ 08 сентября 2011

Проблема здесь заключается в том, что вы запускаете потоки, и они не полностью завершены к моменту вашего вызова для записи вывода на консоль.

Цикл начинается с i нитей, мы представим, что пчелы собирают их как работу, а не все едут к одному источнику пищи, поэтому одни возвращаются дольше, чем другие; затем, возвращаясь в улей, мы вдруг говорим: «Эй, пчелы, мне нужно количество сотрудников!», «... 1, 2, 3 ... только три?» Нет, некоторые i-3 все еще крадутся вокруг!

Итак, идея в том, что у нас должен быть индикатор того, когда работа завершена, или своего рода сигнал самонаведения, чтобы вернуть всех пчел в улей для поголовья. Это может быть сделано с помощью Join или ручной проверки состояния (которая, по сути, заставляет вас продержаться до возвращения последнего соседа).

3 голосов
/ 08 сентября 2011

Вы не ждете окончания потоков. Весьма повезло, что многие выполнили свою работу, прежде чем вы выведете счет.

Добавить

        for (int i = 0; i < 100; i++)
        {
            threadArray[i].Join();

        }

до WriteLine, и вы всегда получаете 100.

1 голос
/ 08 сентября 2011

Это не является детерминированным , когда происходит ваш звонок на Count.Это может произойти до того, как завершится какой-либо поток, или после того, как все потоки закончатся или даже где-то посередине.Если вы хотите подождать, пока все потоки не закончатся, вы должны присоединиться к ним.

1 голос
/ 08 сентября 2011

Это классическое состояние гонки , и я считаю, что вам повезло, что вы приблизитесь к 100 к тому моменту, когда вы вызываете Console.WriteLine.

for (int i = 0; i < 100; i++)
{
    threadArray[i].Start();
}
//absolutely no guarantee that **ANY** threads have completed before next line
Console.WriteLine(ope.Count);

Подумайте об использовании CountdownEvent или аналогичного, чтобы выполнить небольшую синхронизацию.

1 голос
/ 08 сентября 2011

Вы должны защитить свой метод получения public int Count { get { return m_count; } } блокировкой, иначе поток B мог бы считывать это значение, в то время как другой поток A обновляет счетчик в вашем методе Add, это может привести к тому, что вы получите несогласованное представление ваших данных. .

0 голосов
/ 08 сентября 2011

Когда вы печатаете счетчик, вы не имеете представления о том, завершены ли потоки или нет. «Правильный» вывод в вашем коде может быть любым числом от 0 до 100.

Чтобы получить 100 в качестве выходных данных, вы не должны получать значение Count, пока все потоки не завершатся. Вы можете сделать это, вызвав Thread.Join во всех потоках или (что еще лучше) использовать что-то вроде Parallel.For для запуска потоков:

Parallel.For(0, 100, idx => ope.Add);
// When we reach this line, we know all threads have completed so we can 
// safely get the count
var count = ope.Count;
0 голосов
/ 08 сентября 2011

Не уверен, что это ответ, который вы ищете, но чтобы увеличить счетчик потокобезопасным способом, вы должны использовать Interlocked.Increment документы

...