c# 2.0 Является ли int32 потокобезопасным?

Я хочу увеличить целое число, которое увеличивается в обработчике событий таймера и считывается основным и другими рабочими потоками, то есть одним потоком записи и несколькими потоками чтения. будет ли это потокобезопасным?

У меня есть таймер в моем приложении, который запускается каждые 5 секунд

MyClock = new System.Threading.Timer( new TimerCallback(this.Ticker), null, Timeout.Infinite, Timeout.Infinite );

что я включаю вот так MyClock.Change( 5000, 5000 );

Если я увеличиваю целое число в обработчике Ticker, например, tickerCounter++;

Могу ли я тогда сделать доступ только для чтения из основного потока или рабочих потоков одного и того же приложения? Будет ли это потокобезопасно? Есть ли у читателя шанс прочитать частичное значение? или вызывает исключение потоков?


person bsobaid    schedule 11.05.2012    source источник


Ответы (3)


Увеличение вот так

tickerCounter++;

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

Если только один поток изменяет значение, а многие потоки считывают значение, то tickerCounter++ является потокобезопасным в том смысле, что ни один поток никогда не подвергнется частичному чтению. Конечно, есть еще состояние гонки, но даже если вы используете Interlocked, гонка будет.

person David Heffernan    schedule 11.05.2012

Во-первых: прочитайте сообщение в блоге Эрика Липперта: Что вы называете "поточно-безопасным"? Это изменит ваше представление о безопасности потоков и то, как вы будете задавать подобные вопросы в будущем.

Чтения будут атомарными — вы никогда не увидите «половину» обновления — но вы не обязательно увидите последнее значение. (Это означает, что при следующем приращении может появиться "старое" (меньшее) значение для увеличения, конечно.)

Кроме того, само по себе приращение не является безопасным по своей сути - если бы несколько потоков увеличивали одновременно, все они могли бы прочитать одно и то же значение, затем увеличить локально, а затем записать новое значение - так что результатом может быть приращение 1, даже если 10 потоков увеличивались.

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

person Jon Skeet    schedule 11.05.2012
comment
спасибо, для меня это нормально, если значение не является последним, если оно не вызывает исключение или не обеспечивает половинное обновление. - person bsobaid; 11.05.2012
comment
Несоблюдение совета Джона означает, что иногда ваш вызов tickerCounter++; не увеличит значение ticketCounter. В документации для Interlocked.Increment есть пример кода, демонстрирующий эту проблему. Мне кажется, что такое решение приведет к путанице и потерям времени на отладку позже (либо через 1 месяц, когда кто-то еще отлаживает ваш код, либо через 3 месяца, когда вы отлаживаете свой код). - person Brian; 12.05.2012
comment
@ Брайан Хотя я, конечно, не согласен почти со всем, что вы сказали, есть только один поток, выполняющий приращение. Все остальные темы читают, поэтому ваше первое предложение не актуально. - person Marc; 12.05.2012
comment
@Marc: Кто сказал, что увеличивается только один поток? Это System.Threading.Timer без объекта синхронизации, поэтому каждое приращение будет происходить в потоке пула потоков, но каждый раз это может быть другой поток пула потоков. - person Jon Skeet; 12.05.2012
comment
@JonSkeet Я признаю, ты прав. Это может (выполнять приращение одновременно в нескольких потоках тиков таймера) произойти. - person Marc; 12.05.2012
comment
@Marc: Это не обязательно должно быть одновременно. Это просто должно быть в нескольких потоках без гарантированных барьеров памяти, гарантирующих, что значение, прочитанное вторым увеличивающим потоком, было значением, которое было записано первым увеличивающим потоком. Я имею в виду, что вероятно все будет хорошо, но эй :) - person Jon Skeet; 12.05.2012

Возможно, вы могли бы использовать Interlocked.Increment(ref Int32) для увеличения? Это делает это как атомарную операцию.

person Tim    schedule 11.05.2012