memcpy [или нет?] и многопоточность [std::thread из C++11]

Я пишу программное обеспечение на C/C++, используя много BIAS/Profil, библиотеку интервальной алгебры. В моем алгоритме у меня есть мастер, который делит домен и передает его части в подчиненный процесс (ы). Они возвращают статут об этих частях домена. Есть общие данные для чтения и все.

Мне нужно распараллелить мой код, однако, как только 2 подчиненных потока запущены (или, я полагаю, больше), и обе они вызывают функции этой библиотеки, происходит сбой. Что характерно для этих segfaults, так это то, что gdb редко указывает на одну и ту же строку ошибки из двух сборок: это зависит от скорости потоков, если она была запущена ранее, и т. Д. Я пытался заставить потоки выдавать результаты до тех пор, пока мастер, он "стабилизирует" ошибку. Я вполне уверен, что это происходит из-за обращений к memcpy из библиотеки (после обратной трассировки gdb я всегда заканчиваю тем, что функция BIAS / Profil вызывает memcpy. Честно говоря, почти все функции вызывают memcpy для временного объект до возврата результата...). Из того, что я читал в Интернете, может показаться, что memcpy() может быть не поточно-ориентированным, в зависимости от реализаций (особенно здесь). (Это кажется странным для функции, предназначенной только для чтения общих данных... или, может быть, при записи потоковых данных оба потока используют одну и ту же область памяти?)

Чтобы попытаться решить эту проблему, я бы хотел "заменить" (по крайней мере, для тестов, если поведение изменилось) вызов memcpy для вызова с мьютексной рамкой. (что-то вроде mtx.lock (); mempcy (...); mtx.unlock ();)

1-й вопрос: я вообще не являюсь инженером / разработчиком кода, и мне не хватает базовых знаний. Я думаю, что, поскольку я использую предварительно собранную библиотеку BIAS/Profil, вызванный memcpy является той системой, на которой была построена библиотека, правильно? Если это так, изменилось бы что-нибудь, если бы я попытался собрать библиотеку из исходного кода в моей системе? (Я не уверен, что смогу построить эту библиотеку, поэтому вопрос.)

2-й вопрос: в моем string.h memcpy объявлен:#ifndef __HAVE_ARCH_MEMCPYextern void * memcpy(void *,const void *,__kernel_size_t);#endif и в некоторых других строковых заголовках (string_64.h, string_32.h) определение формы: #define memcpy(dst, src, len) __inline_memcpy((dst), (src), (len)) или какое-то более явное определение, или просто объявление, подобное приведенному. Это начинает уродливо, но в идеале я хотел бы создать переменную препроцессора #define __HAVE_ARCH_MEMCPY 1и void * memcpy(void *,const void *,__kernel_size_t) который сделал бы memcpy в рамке мьютекса с отклоненным memcpy. Идея здесь состоит в том, чтобы избежать путаницы с библиотекой и заставить ее работать с 3 строками кода;)

Есть идея получше? (это сделало бы мой день...)

3 ответа

Решение

Учитывая, что ваши наблюдения, и что Profil lib из прошлого тысячелетия, и что документация (домашняя страница и Profil2.ps) даже не содержат слова "поток", я бы предположил, что библиотека не является потокобезопасным.

1-й: нет, обычно memcpy является частью libc, которая динамически связана (по крайней мере, в настоящее время). На Linux, проверьте с ldd NAMEOFBINARY, который должен дать строку с чем-то вроде libc.so.6 => /lib/i386-linux-gnu/libc.so.6 или похожие. Если нет: восстановите. Если да: восстановление может помочь в любом случае, так как есть много других факторов.

Помимо этого, я думаю, memcpy Потокобезопасен до тех пор, пока вы никогда не будете записывать данные (даже запись неизмененных данных повредит: https://blogs.oracle.com/dave/entry/memcpy_concurrency_curiosities).

2-й: если окажется, что вы должны использовать модифицированный memcpyтакже подумайте о LD_PRELOAD,

ИМХО, вам следует сосредоточиться не на функциях memcpy (), а на функциональности более высокого уровня.

И memcpy () является поточно-ориентированным, если обрабатываемые интервалы памяти параллельно работающих потоков не перекрываются. Практически, в memcpy () есть только цикл for(;;) (с большим количеством оптимизаций) [по крайней мере, в glibc], это причина, почему она объявлена.

Если вы хотите знать, что будут делать ваши параллельные потоки memcpy (), вы должны представить циклы for(;;), которые копируют память через указатели longint.

В общем случае вы должны использовать критическую секцию, мьютекс или какую-либо другую технику защиты, чтобы не допустить одновременного доступа нескольких потоков к функциям, не поддерживающим потоки. Некоторые реализации ANSI C memcpy() не являются потокобезопасными, некоторые ( безопасно, не безопасно)

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

Другие вопросы по тегам