2014-09-14 3 views
0

У меня есть 2 вопроса относительно нитей, один - о состоянии гонки, а другой - о мьютексе. Поэтому первый вопрос: Я прочитал о расе состоянии в википедии странице: http://en.wikipedia.org/wiki/Race_conditionСостояние гонки и мьютексы

И в примере гонки состоянии между 2 нитями это показано на рисунке: http://i60.tinypic.com/2vrtuz4.png[

Сейчас до сих пор я считал, что потоки работают параллельно друг другу, но, судя по этой картине, кажется, что я интерпретировал, как действия, выполняемые компьютером, ошибочны. С этой картинки выполняется только одно действие за раз, и хотя потоки время от времени переключаются, а другой поток выполняет некоторые действия, это все равно 1 действие за раз, выполненное компьютером. Неужели это так? Нет «реальных» параллельных вычислений, всего лишь одно действие за один раз с очень высокой скоростью, что дает иллюзию параллельных вычислений?

Это приводит меня к моему второму вопросу о мьютексе. Я читал, что если потоки читают/записывают в одну и ту же память, нам нужен какой-то механизм синхронизации. Я читал, что обычные типы данных не будут делать, и нам нужен мьютекс. Давайте возьмем для примера следующего кода:

#include <stdio.h> 
#include <stdbool.h> 
#include <windows.h> 
#include <process.h> 

bool lock = false; 

void increment(void*); 
void decrement(void*); 

int main() 
{ 
    int n = 5; 
    HANDLE hIncrement = (HANDLE)_beginthread(increment, 0, (void*)&n); 
    HANDLE hDecrement = (HANDLE)_beginthread(decrement, 0, (void*)&n); 
    WaitForSingleObject(hIncrement, 1000 * 500); 
    WaitForSingleObject(hDecrement, 1000 * 500); 
    return 0; 
} 

void increment(void *p) 
{ 
    int *n = p; 
    for(int i = 0; i < 10; i++) 
    { 
     while (lock) 
     { 

     } 
     lock = true; 
     (*n)++; 
     lock = false; 
    } 
} 

void decrement(void *p) 
{ 
    int *n = p; 
    for(int i = 0; i < 10; i++) 
    { 
     while (lock) 
     { 

     } 
     lock = true; 
     (*n)--; 
     lock = false; 
    } 
} 

Теперь в моем примере здесь, я использую замок Его как мой механизм синхронизации, чтобы избежать состояний гонки между 2 нитями над пространством памяти, указываемым указателем п. Теперь то, что я здесь сделал, очевидно, не будет работать, потому что, хотя я избежал условия гонки по пространству памяти, указанному указателем n между двумя потоками, может возникнуть новое состояние гонки по переменной блокировки bool.

Давайте рассмотрим следующую последовательность событий (А = прибавка нить, B = декремент нить):

  • А получает из цикла в то время, так как замок является ложным
  • А получает установить блокировку на истинный
  • B ожидает в то время цикла, потому что замок установлен в положении истинного
  • приращения значения указывало на п
  • А установлена ​​замок ложную
  • А получает к петле в то время как
  • А получает из цикла в то время, так как замок является ложным
  • Б выходит из цикла в то время, так как замок является ложным
  • А установлена ​​блокировка истинных
  • наборов B блокировки для правда

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

Хорошо, это мое понимание и решение нашей проблемы выше, нам нужно мьютекс. Я в порядке с этим, тип данных, который волшебным образом будет служить доказательством гонки. Я просто не понимаю, как с помощью метода mutex этого не произойдет, когда, как и в случае с любым другим типом, он будет и здесь будет моей проблемой, я хочу понять, почему мьютекс и как это происходит.

+2

Вы можете * иметь несколько потоков, работающих одновременно в многопроцессорной системе. – EOF

+0

Вы не можете использовать SO, чтобы изучить новые знания, такие как потоки. Я предлагаю вам найти хороший сайт и учебник и начать учиться. Ваш вопрос показывает, что вы абсолютно не знаете, что такое потоки или как это работает. –

+0

Я интерпретирую это как вполне правильный вопрос, хотя и с большим количеством дополнительной экспозиции вокруг него, что, вероятно, является результатом слабого смущения незнакомого контекста (я замечаю ту же тенденцию в своих собственных вопросах). Итак, я думаю, что @ Omlis532 - это вопрос: «Как может существовать тип mutex threadafe, когда все базовые типы не являются потокобезопасными?» – Medo42

ответ

1

О первом вопросе: есть ли на самом деле несколько разных потоков, запущенных сразу или он просто реализован как быстрое переключение, является вопросом вашего оборудования. Типичные ПК в наши дни имеют несколько ядер (часто с более чем одним потоком каждый), поэтому вы должны предположить, что все на самом деле происходит в одно и то же время.

Но даже если у вас только одноядерная система, все не так просто. Это связано с тем, что компилятору обычно разрешено повторно заказывать инструкции для оптимизации кода. Он также может, например, выберите кэширование переменной в регистре CPU вместо загрузки ее из памяти каждый раз, когда вы обращаетесь к ней, и ей также не нужно записывать ее обратно в память каждый раз, когда вы пишете эту переменную. Компилятору разрешено это делать до тех пор, пока результат будет таким же, как если бы он выполнил исходный код в первоначальном порядке - пока никто не смотрит внимательно на то, что происходит на самом деле, например, на другой поток.

И как только у вас действительно есть разные ядра, учтите, что все они имеют свои собственные регистры процессора и даже свой собственный кеш. Даже если поток на одном ядре написал определенную переменную, пока это ядро ​​не записывает свой кеш обратно в общую память, другое ядро ​​не увидит этого изменения.

Вкратце, вы должны быть очень осторожны при принятии каких-либо предположений о том, что происходит, когда два потока обращаются к переменным одновременно, особенно в C/C++. Взаимодействия могут быть настолько удивительными, что я бы сказал, чтобы оставаться в безопасности, вы должны убедиться, что в вашем коде нет условий гонки. всегда используя мьютексы для доступа к памяти, которая совместно используется потоками.

Это то, где мы можем аккуратно переходить во второй вопрос: что особенного в отношении мьютексов и как они могут работать, если все основные типы данных не являются потокобезопасными?

Что касается мьютексов, то они реализованы с большим объемом знаний о системе, в которой они используются (аппаратное обеспечение и операционная система), а также с прямой помощью или глубоким знанием самого компилятора.

Язык C не дает вам прямого доступа ко всем возможностям вашего оборудования и операционной системы, потому что платформы могут сильно отличаться друг от друга. Вместо этого C фокусируется на предоставлении уровня абстракции, который позволяет вам скомпилировать один и тот же код для разных платформ. Различные «базовые» типы данных - это всего лишь то, что C-стандарт придумал как набор типов данных, которые могут быть каким-то образом поддерживаться практически на любой платформе, - но фактическое оборудование, которое будет скомпилировано вашей программой, обычно не ограничено к этим типам и операциям.

Другими словами, не все, что вы можете сделать с вашим компьютером, может быть выражено в терминах int, байтов, присвоений, арифметических операторов и т. Д. Например, ПК часто вычисляют с помощью 80-битных типов с плавающей запятой, которые обычно не отображаются непосредственно на тип с плавающей точкой C. Кроме того, есть также указания процессора, которые влияют на то, как много процессорных ядер будут работать вместе. Кроме того, если вы знаете CPU, вы часто знаете несколько вещей о поведении базовых типов, которые стандарт C не гарантирует (например, являются ли нагрузки и хранилища для 32-разрядных целых чисел атомарными). Благодаря этим дополнительным знаниям, можно реализовать мьютексы для этой конкретной платформы, и для этого часто требуется код, например. написанные непосредственно на языке ассемблера, поскольку необходимые функции недоступны в обычном C.

+0

Как вы думаете, вы могли бы связать меня с хорошим учебным пособием, используя мьютексы и потоки? Я создаю потоки с использованием _beginthread и набираю его в РУЧКУ, поэтому я смотрел онлайн, но я не мог найти кого-то, который использует тип _beginthread, отбрасывая его в HANDLE и используя мьютексы, поэтому я не думаю, к моему коду. Было бы очень признательно! – Omlis532

+0

Я никогда не делал многопоточность с этим API, но есть документация MSDN об этом на http://msdn.microsoft.com/de-de/library/y6h8hye8.aspx. В примерной программе они используют функции 'CreateMutex',' WaitForSingleObject' и 'ReleaseMutex'. Вы должны знать, что эти мьютексы являются кросс-процессами, которые часто больше, чем вы хотите/нуждаетесь. – Medo42

+0

означает, что поток, созданный API, представляет собой потоки в разных ядрах или ...? – Omlis532

Смежные вопросы