Синхронизация потоков

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

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

Почему это происходит. Я правильно блокирую и разблокирую один и тот же мьютекс.

Как синхронизировать темы?

РЕДАКТИРОВАТЬ:

Я объявляю мьютексы и переменные здесь

static pthread_mutex_t movementIdMutex = PTHREAD_MUTEX_INITIALIZER;
static int nav_movementId = 0;

static pthread_mutex_t newMovementMutex = PTHREAD_MUTEX_INITIALIZER;
static int nav_newMovement = 0;

Я устанавливаю переменные здесь

void nav_setMovementIdentifier(int id)
{
printf("Received movement id:%d from connectivity\n", id);

int result; /* Use the result for testing */

result = pthread_mutex_lock(&movementIdMutex);
nav_movementId = id;
printf("nav_movementId is %d\n", nav_movementId);
result = pthread_mutex_unlock(&movementIdMutex);

result = pthread_mutex_lock(&newMovementMutex);
nav_newMovement = 1;
printf("nav_newMovement is %d in the setId function\n", nav_newMovement);
result = pthread_mutex_unlock(&newMovementMutex);
printf("\n");
}

и прочитайте их здесь

void *startConnectivityListener(void *ptr)     {

/* Declare safety variables because segments cant be locked/unlocked 
*  using mutexes if they are checking in statement such as if and while.
*/
int newMoveCheck = 0;
int startIndoorCheck = 0;
int startOutdoorCheck = 0;

int listening = 1;

while(listening == 1)
{
    int result; /* Use for testing */

    /* check if a new movement command waits */
    result = pthread_mutex_lock(&newMovementMutex);
    newMoveCheck = nav_newMovement;
    printf("nav new movement in thread is :%d\n", nav_newMovement);
    printf("newMoveCheck in mutex lock is:%d\n", newMoveCheck);
    result = pthread_mutex_unlock(&newMovementMutex);

    result = pthread_mutex_lock(&movementIdMutex);
    printf("nav_movementId in thread is %d\n", nav_movementId);
    result = pthread_mutex_unlock(&movementIdMutex);

    printf("newMoveCheck is %d\n", newMoveCheck);
    sleep(1);

    if(newMoveCheck == 1)

Я получаю правильные распечатки в операторах setter functions printf, он устанавливает значения для переданного id и 1.

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


person some_id    schedule 16.05.2011    source источник
comment
Нужно увидеть некоторый код, чтобы мы могли сказать, где может быть проблема.   -  person onteria_    schedule 16.05.2011
comment
Можете ли вы предоставить компилируемый пример, который воспроизводит проблему?   -  person janneb    schedule 16.05.2011
comment
Я добавил соответствующий код. :)   -  person some_id    schedule 16.05.2011


Ответы (4)


Попробуйте добавить спецификатор volatile к переменным nav_movementId и nav_newMovement.

person arrowd    schedule 18.05.2011
comment
В конце концов, проблема заключалась в том, что сами процессы были разветвлены, поэтому у них было 2 копии программ, работающих в их собственных процессах, и одна из них обновляла свои собственные переменные, а другая считывала свои собственные, не обновленные переменные. При печати адресов памяти они были одинаковыми на основе адреса виртуальной памяти, который был создан до разветвления процессов. странная установка :Р - person some_id; 18.05.2011

Эти декларации:

static int nav_movementId = 0;
static int nav_newMovement = 0;

создавать совершенно новые и разные переменные - поэтому вы не видите изменений глобальной переменной. Сделайте объявление таким, чтобы функция, которая обновляет переменные, воздействовала на глобальные экземпляры:

extern int nav_movementId;
extern int nav_newMovement;

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

В одном модуле:

pthread_mutex_t movementIdMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t newMovementMutex = PTHREAD_MUTEX_INITIALIZER;

В заголовке (или другом модуле):

extern pthread_mutex_t movementIdMutex;
extern pthread_mutex_t newMovementMutex;
person Michael Burr    schedule 16.05.2011
comment
Переменные и мьютексы объявлены в файле .c, и весь код потока находится в том же файле. Так что теоретически переменные должны быть доступны для всех потоков. будут ли статические переменные мьютекса разными копиями? То, что должно быть сделано? Должен ли я удалить статические объявления мьютексов и переменных? - person some_id; 16.05.2011
comment
Что, если код запускается в собственном процессе и включает файл .h или corelogic.c, который является частью другого процесса. У меня есть один процесс, выполняющий кучу кода, который включает заголовок модуля, работающего в другом процессе. Что такое код в процессе 1, который вызывает код в процессе 2 и устанавливает переменные, присутствующие в коде, который находится в процессе 2? Есть ли у процесса 1 копия всего кода, подключенного через файл .h, который он включает из модулей процесса 2? Итак, есть 2 копии переменных, по 1 для каждого процесса? Нельзя ли установить переменные процесса 2 из процесса 1? - person some_id; 16.05.2011
comment
Если вы говорите, что corelogic.c включен в более чем один исполняемый образ и/или вы запускаете более одной копии одного и того же исполняемого файла, тогда ответ таков: существует одна копия переменных для каждого запущенного процесса. Вы не можете разделить память между процессами, просто объявив одну и ту же переменную в каждом из них. - person AShelly; 16.05.2011
comment
Мы используем разделяемую память в linux (ubuntu), но что произойдет, если процесс 1 включает файл .h только для вызова из него функций. И файл .h, и остальная часть кода для этой второй подсистемы выполняются в процессе 2. - person some_id; 16.05.2011
comment
Извините, я не понял, что весь этот код был в одном модуле, мой ответ не имеет смысла в этом сценарии. Я также не понимал, что мьютексы и переменные распределяются между процессами. Я не сталкивался с таким использованием - все, что я знаю, это то, что вам нужно будет инициализировать мьютексы с атрибутами, указывающими, что они совместно используются отдельными процессами. Я не знаю, подходит ли значение по умолчанию PTHREAD_MUTEX_INITIALIZER. Возможно, вам потребуется установить для атрибута мьютекса значение PTHREAD_PROCESS_SHARED с помощью pthread_mutexattr_setpshared(). - person Michael Burr; 16.05.2011

Эти статические мьютексы и переменные находятся в том же исходном файле, что и приведенный ниже код, или они находятся в заголовке? Если они находятся в заголовке и эти две функции находятся в отдельных файлах, то каждая из них имеет свою собственную копию мьютекса и переменной. Вам нужно сделать их extern вместо static.

Еще одно: функция «слушатель» на самом деле не ждет, пока переменные будут установлены другой функцией - мьютекс только гарантирует, что они не будут активно записываться. Так что я не удивлюсь, если в первый раз, когда вы прочитаете nav_newMovement в прослушивателе, это все еще будет 0. Здесь может помочь небольшое объяснение того, как вы ожидаете, что система будет работать.

person AShelly    schedule 16.05.2011
comment
Система должна работать так, чтобы модуль corelogic.c содержал в себе все переменные, мьютексы, код потока. Другие модули импортируют файл заголовка, чтобы они могли использовать функции для установки значений переменных. У меня есть несколько запущенных потоков, и один из них считывает значения переменных, установленных из вызова функции, о котором я упоминал. Проблема в том, что переменные обновляются при установке, но не отображают обновленные значения при чтении. Подозреваю, что там 2 набора этих переменных что ли, 2 копии. - person some_id; 16.05.2011
comment
Обратите внимание, что printf буферизован и не совсем потокобезопасен. Вы можете не получить распечатки в том порядке, в котором они появились. Читатель в петле? - вроде так, но код не полный. Если модуль записи не вызывается до тех пор, пока не будет запущен цикл чтения, я ожидаю увидеть несколько значений newMoveCheck равных 0, прежде чем вы увидите 1. Если это тестовый код, выходящий из первого 1, вы можете никогда не увидеть окончательный printf вообще. - person AShelly; 16.05.2011
comment
Пожалуйста, смотрите мой второй комментарий к ответу Майкла. - person some_id; 16.05.2011

Порядок выполнения потоков не определен, то есть то, что вы запустили поток, не означает, что порядок выполнения потоков будет в том порядке, в котором вы их запустили. Чтобы синхронизировать потоки, вы должны создать «барьеры», то есть точку в коде, где один поток не может продолжаться, пока другой поток не достигнет определенной точки. Обычно это делается с помощью семафоров, хотя вы можете сделать это с помощью мьютексов и условных переменных.

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

  1. Поток № 1 инициализирует семафор № 1 как заблокированный, а семафор № 2 — как разблокированный.
  2. Поток № 1 запускает поток № 2, который затем блокирует семафор № 2 и продолжает работу.
  3. Тем временем поток № 1 пытается заблокировать уже заблокированный семафор № 1, поэтому он остается заблокированным, пока работает поток № 2.
  4. Поток № 2 завершен и разблокирует семафор № 1 ... Теперь поток № 1 продолжается.
  5. Поток № 2 пытается заблокировать семафор № 2, но он заблокирован, поэтому он должен дождаться завершения работы потока № 1.
  6. Поток №1 завершает работу и разблокирует семафор №2... Поток №2 теперь продолжается
  7. Поток № 1 пытается заблокировать семафор № 1, но он заблокирован, поэтому он должен дождаться завершения работы потока № 2.
  8. Продолжайте повторять, пока не дойдете до какого-то конечного состояния...

Таким образом, в основном поток № 1 разблокирует поток № 2, и наоборот. Поток № 1 не разблокирует себя, а затем не блокирует поток № 2 или какой-либо другой вариант, когда один поток должен одновременно блокировать и разблокировать семафор для себя и другого потока. Таким образом, вы не столкнетесь с точкой пересечения, когда ни один поток не заблокирован, и оба они работают бесплатно, или, что еще хуже, состояние тупиковой блокировки, когда оба потока ждут, пока другой поток разблокирует себя.

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

person Jason    schedule 16.05.2011