Проблема
Гонка данных происходит когда:
Чтобы найти этот дефект, необходимо задать многозадачные опции перед анализом. Чтобы задать эти опции, на панели Configuration, выбирают Multitasking. Для получения дополнительной информации смотрите Анализ Многозадачности Polyspace Конфигурирования Вручную.
Риск
Гонка данных может привести к непредсказуемым значениям совместно используемой переменной, потому что вы не управляете порядком операций в различных задачах.
Гонки данных между двумя операциями записи более серьезны, чем гонки данных между записью и операцией чтения. Две операции записи могут вмешаться друг в друга и привести к неопределенным значениям. Чтобы идентифицировать конфликты записи записи, используйте фильтры на столбце Detail панели Results List. Для этих конфликтов столбец Detail показывает дополнительную линию:
Variable value may be altered by write-write concurrent access.
Смотрите
результаты фильтра и группы.
Фиксация
Чтобы зафиксировать этот дефект, защитите операции на совместно используемой переменной с помощью критических разделов, временного исключения, или другой означает. Смотрите Меры защиты для Совместно используемых переменных в Многозадачном Коде.
Чтобы идентифицировать существующие меры защиты, которые можно снова использовать, см. таблицу и графики, сопоставленные результатом. Таблица показывает каждую пару конфликтных вызовов. Столбец Access Protections показывает существующие меры защиты на вызовах. Чтобы видеть, что последовательность вызова функции ведет к конфликтам, кликните по значку. Для примера смотрите ниже.
Пример - незащищенная операция на глобальной переменной от нескольких задач
int var;
void begin_critical_section(void);
void end_critical_section(void);
void increment(void) {
var++;
}
void task1(void) {
increment();
}
void task2(void) {
increment();
}
void task3(void) {
begin_critical_section();
increment();
end_critical_section();
}
В этом примере, чтобы эмулировать многозадачное поведение, задают следующие опции:
На командной строке можно использовать следующее:
polyspace-bug-finder
-entry-points task1,task2,task3
-critical-section-begin begin_critical_section:cs1
-critical-section-end end_critical_section:cs1
В этом примере, задачи task1
, task2
, и task3
вызовите функциональный increment
. increment
содержит операцию var++
это может включить несколько машинных команд включая:
Эти машинные команды, когда выполняется от task1
и task2
, может произойти одновременно в непредсказуемой последовательности. Например, чтение var
от task1
может произойти или прежде или после записи в var
от task2
. Поэтому значение var
может быть непредсказуемым.
Хотя task3
вызовы increment
в критическом разделе другие задачи не используют тот же критический раздел. Операции в критическом разделе task3
не являются взаимоисключающими с операциями в других задачах.
Поэтому эти три задачи работают с совместно используемой переменной без общей защиты. В ваших деталях результата вы видите каждую пару конфликтных вызовов функции.
Если вы кликаете по значку, вы видите, что последовательность вызова функции запускается от точки входа до операции чтения или операции записи. Вы также видите что операция, начинающая с task3
находится в критическом разделе. Запись Access Protections показывает блокировку, и разблокируйте функцию, которые начинают и заканчивают критический раздел. В этом примере вы видите функции begin_critical_section
и end_critical_section
.
Коррекция — операция места в критическом разделе
Одна возможная коррекция должна поместить операцию в критический раздел. Можно реализовать критический раздел несколькими способами. Например:
Можно поместить var++
в критическом разделе. Когда task1
вводит ее критический раздел, другие задачи не могут ввести свои критические разделы до task1
оставляет его критический раздел. Операция var++
от этих трех задач не может вмешаться друг в друга.
Реализовывать критический раздел, в функциональном increment
, поместите операцию var++
между вызовами begin_critical_section
и end_critical_section
.
int var;
void begin_critical_section(void);
void end_critical_section(void);
void increment(void) {
begin_critical_section();
var++;
end_critical_section();
}
void task1(void) {
increment();
}
void task2(void) {
increment();
}
void task3(void) {
increment();
}
Можно поместить вызов increment
в том же критическом разделе в этих трех задачах. Когда task1
вводит ее критический раздел, другие задачи не могут ввести свои критические разделы до task1
оставляет его критический раздел. Вызовы increment
от этих трех задач не может вмешаться друг в друга.
Реализовывать критический раздел, в каждой из этих трех задач, increment
вызова
между вызовами begin_critical_section
и end_critical_section
.
int var;
void begin_critical_section(void);
void end_critical_section(void);
void increment(void) {
var++;
}
void task1(void) {
begin_critical_section();
increment();
end_critical_section();
}
void task2(void) {
begin_critical_section();
increment();
end_critical_section();
}
void task3(void) {
begin_critical_section();
increment();
end_critical_section();
}
Коррекция — делает задачи временно исключительными
Другая возможная коррекция должна сделать задачи, task1
, task2
и task3
, временно исключительный. Временно исключительные задачи не могут выполниться одновременно.
На панели Configuration задайте следующие дополнительные опции:
На командной строке можно использовать следующее:
polyspace-bug-finder
-temporal-exclusions-file "C:\exclusions_file.txt"
где файл
C:\exclusions_file.txt
имеет следующую линию:
Пример - незащищенная операция в потоках, созданных с pthread_create
#include <pthread.h>
pthread_mutex_t count_mutex;
long long count;
void* increment_count(void* args)
{
count = count + 1;
return NULL;
}
void* set_count(void *args)
{
long long c;
c = count;
return NULL;
}
int main(void)
{
pthread_t thread_increment;
pthread_t thread_get;
pthread_create(&thread_increment, NULL, increment_count, NULL);
pthread_create(&thread_get, NULL, set_count, NULL);
pthread_join(thread_get, NULL);
pthread_join(thread_increment, NULL);
return 1;
}
В этом примере Средство поиска Ошибки обнаруживает создание, разделяют потоки pthread_create
. Дефект Data race повышен потому что операция count = count + 1
в потоке с ID thread_increment
конфликты с операцией c = count
в потоке с ID thread_get
. Переменная count
получен доступ в нескольких потоках без общей защиты.
Две конфликтных операции являются неатомарными. Операция c = count
является неатомарным на 32-битных целях. Смотрите Задают Атомарные Операции в Многозадачном Коде.
Коррекция — защищает операции с pthread_mutex_lock
и pthread_mutex_unlock
Пара
Предотвратить параллельный доступ на переменной count
, защитите операции на count
с критическим разделом. Используйте функции pthread_mutex_lock
и pthread_mutex_unlock
реализовывать критический раздел.
#include <pthread.h>
pthread_mutex_t count_mutex;
long long count;
void* increment_count(void* args)
{
pthread_mutex_lock(&count_mutex);
count = count + 1;
pthread_mutex_unlock(&count_mutex);
return NULL;
}
void* set_count(void *args)
{
long long c;
pthread_mutex_lock(&count_mutex);
c = count;
pthread_mutex_unlock(&count_mutex);
return NULL;
}
int main(void)
{
pthread_t thread_increment;
pthread_t thread_get;
pthread_create(&thread_increment, NULL, increment_count, NULL);
pthread_create(&thread_get, NULL, set_count, NULL);
pthread_join(thread_get, NULL);
pthread_join(thread_increment, NULL);
return 1;
}