Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.15.240.22] |
|
Сообщ.
#1
,
|
|
|
Всем привет!
В общем тема дня - сабж. Не знаю как вам, а для меня - мутная тема. Но жысть диктует свои правила ... А ноги растут из все той же си-шной либы libssh2. На сколько мне хватило моего скромного IQ - она знать не знает о многопоточности. Залез в исходники. По-грипал по слову "mutex" - по нулям. А вот по слову "thread" нашел два более-менее совпадения в коде: src/agent.c snprintf(mapname, sizeof(mapname), "PageantRequest%08x%c", (unsigned)GetCurrentThreadId(), '\0'); // ◄──────────────────────── GetCurrentThreadId() filemap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, PAGEANT_MAX_MSGLEN, mapname); Ну а потом еще в комментах исходников обнаружил следующее: include/libssh2.h /* * libssh2_init() * * Initialize the libssh2 functions. This typically initialize the * crypto library. It uses a global state, and is not thread safe ◄──────────────────────── "not thread safe " * you must make sure this function is not called concurrently. * * Flags can be: * 0: Normal initialize * LIBSSH2_INIT_NO_CRYPTO: Do not initialize the crypto library (ie. * OPENSSL_add_cipher_algoritms() for OpenSSL * * Returns 0 if succeeded, or a negative value for error. */ И тут я вспомнил, что недавно, перед сном я читал какую-то бадягу, уже засыпая ... и там был термин, как его ... во, он - thread_local, из С++. Читаем: Цитата The thread_local keyword is only allowed for objects declared at namespace scope, objects declared at block scope, and static data members. It indicates that the object has thread storage duration. It can be combined with static or extern to specify internal or external linkage (except for static data members which always have external linkage), respectively, but that additional static doesn't affect the storage duration. Короч, оч много слов интуристов! Но, как я понял, описатель thread_local сразу же подразумевает static (не? пруф?) Едем дальше ... у нас же на анатомическом столе си-либа (нам плюсами и не пахнет). Включаем глубинное гугление... Находим занятное чтиво: Цитата Aside from that, various compiler implementations provide specific ways to declare thread-local variables: Solaris Studio C/C++, IBM XL C/C++,[3] GNU C,[4] Clang[5] and Intel C++ Compiler (Linux systems)[6] use the syntax: __thread int number; Visual C++,[7] Intel C/C++ (Windows systems),[8] C++Builder, and Digital Mars C++ use the syntax: __declspec(thread) int number; C++Builder also supports the syntax: int __thread number; On Windows versions before Vista and Server 2008, __declspec(thread) works in DLLs only when those DLLs are bound to the executable, and will not work for those loaded with LoadLibrary() (a protection fault or data corruption may occur). Ну и добрались до самих органов В либе libssh2: Собственно, вопрос: а если заменить все описатели static на аналоги c thread_local, то можно ли данную либу использовать в многопоточной среде (в более чем одном потоке)? Или что-то есть еще важное, что я тупо упускаю? |
Сообщ.
#2
,
|
|
|
Цитата JoeUser @ Не. Пруф прям в цитате: они могут иметь linkage как external, так и internal. Это просто объекты, локальные в потоке. Т.е. каждая нитка, обращаясь к ним по имени, будет ссылаться на свои собственные объекты.Короч, оч много слов интуристов! Но, как я понял, описатель thread_local сразу же подразумевает static (не? пруф?) Цитата JoeUser @ Собсвтенно поэтому и нет. thread_local нужно делать любые глобальные объекты, которые хранят состояния, которые могут быть разрушены гонкой потоков. Собственно, вопрос: а если заменить все описатели static на аналоги c thread_local, то можно ли данную либу использовать в многопоточной среде (в более чем одном потоке)? |
Сообщ.
#3
,
|
|
|
Цитата Qraizer @ Не Вот народ приводит цитаты из стандарта: Цитата When thread_local is applied to a variable of block scope the storage-class-specifier static is implied if it does not appear explicitly Цитата All variables declared with the thread_local keyword have thread storage duration. The storage for these entities shall last for the duration of the thread in which they are created. There is a distinct object or reference per thread, and use of the declared name refers to the entity associated with the current thread |
Сообщ.
#4
,
|
|
|
JoeUser, как это что-либо меняет? Накидал вот наконец-то пример:
// ------------ thrd.h extern thread_local int trdLocalVar; void g(); // ------------ src1.cpp #include <iostream> #include "thrd.h" void g() { std::cout << "The trdLocalVar is " << trdLocalVar << " now" << std::endl; } // ------------ src2.cpp #include <thread> #include <mutex> #include <iostream> #include "thrd.h" thread_local int trdLocalVar = -1; std::mutex mtx; void thr(int n) { std::lock_guard ct(mtx); trdLocalVar = n; g(); } int main() { std::thread trd1(thr, 1); std::thread trd2(thr, 2); std::thread trd3(thr, 3); trd1.join(); trd2.join(); trd3.join(); } The trdLocalVar is 1 now The trdLocalVar is 2 now The trdLocalVar is 3 now |
Сообщ.
#5
,
|
|
|
Цитата Qraizer @ JoeUser, как это что-либо меняет? Ну просто мне запомнилось, что threal_local "подразумевает статический спецификатор класса хранения". Но я забыл где об этом читал. Решил пререспросить "а если заменить все описатели static на аналоги c thread_local ...". Ты ответил нет. Но все же получается "дa" ... Для однопоточной среды static будет хранить одну копию данных. В многопоточной среде, если использовать thread_local - для каждого потока будет своя копия, аналогичная static - для однопоточной среды. |
Сообщ.
#6
,
|
|
|
И ещё раз нет. static делает имя локальным в единице трансляции, и это не то же самое, что локальное имя в потоке. static обеспечивает локальность имени на основе статического деления программы на единицы трансляции, thread_local – на основе динамического разделения программы на потоки. Поток может вызвать функции из других единиц трансляции, и есть очень большая разница между глобальными объектами с внутренним связыванием и локальными в потоке объектами с внешним связыванием.
Добавлено Вот более детальный пример: // ------------ thrd.h #ifdef THREAD_LOCAL extern thread_local int trdLocalVar; #else static int trdLocalVar = -1; #endif void g(); // ------------ src1.cpp #include <iostream> #include <iomanip> #include <mutex> #include <thread> #include "thrd.h" std::mutex out; void g() { std::lock_guard ct(out); std::cout << std::hex << std::setw(8) << std::this_thread::get_id() << ": the trdLocalVar is " << std::dec << trdLocalVar << " now" << std::endl; } // ------------ src2.cpp #include <thread> #include <mutex> #include <array> #include <iostream> #include "thrd.h" #ifdef THREAD_LOCAL thread_local int trdLocalVar = -1; #endif std::mutex mtx; std::array<std::mutex, 3> events; void thr(int n) { trdLocalVar = n; g(); std::lock_guard ev(events[n-1]); std::lock_guard ct(mtx); g(); } int main() { using namespace std::literals; for (auto &i : events) i.lock(); std::thread trd1(thr, 1); std::thread trd2(thr, 2); std::thread trd3(thr, 3); g(); std::this_thread::sleep_for(2s); events[1].unlock(); trd2.join(); g(); events[0].unlock(); trd1.join(); g(); events[2].unlock(); trd3.join(); g(); } bc4: the trdLocalVar is 1 now f1c: the trdLocalVar is 2 now 198c: the trdLocalVar is 3 now 654: the trdLocalVar is -1 now f1c: the trdLocalVar is 2 now 654: the trdLocalVar is -1 now bc4: the trdLocalVar is 1 now 654: the trdLocalVar is -1 now 198c: the trdLocalVar is 3 now 654: the trdLocalVar is -1 now Добавлено P.S. Вариант с thread_local без extern ничего не поменяет. Всё равно в src1 и src2 это будут разные объекты. |
Сообщ.
#7
,
|
|
|
Цитата Qraizer @ P.S. Вариант с thread_local без extern ничего не поменяет. Всё равно в src1 и src2 это будут разные объекты. Тогда не понимаю, о чем ты споришь Смотри static. Для src1 и src2 - это разные объекты, принадлежащие своим единицам транстяции. thread_local. Для src1 и src2 - это также разные объекты, принадлежащие своим единицам транстяции. Разница лишь во времени существования. static - существуют все время жизни программы. А thread_local - время жизни потока. Пусть есть однопоточная либа SRC и ее однопоточное использование: // src.h ────────────────────────────────────────────────────────────────────────── #ifndef SRC_H #define SRC_H #include <iostream> void set(int i); void prn(); #endif // SRC_H // src.cpp ──────────────────────────────────────────────────────────────────────── #include "src.h" static int State; void set(int i) { State = i; } void prn() { std::cout << State << std::endl; } // main.cpp ─────────────────────────────────────────────────────────────────────── #include <src.h> void func(int i) { set(i); prn(); } int main() { func(12); return 0; } Ничего необычного. Запустили функцию, которая вызвала изменение "состояния библиотеки" и потом печать. Увидели 12. А теперь желаем эту либу использовать в многопоточной среде. В самой "либе" меняем static на thread_local. Ну а основную прогу переписываем под многопоточное исполнение: // src.h ────────────────────────────────────────────────────────────────────────── #ifndef SRC_H #define SRC_H #include <iostream> void set(int i); void prn(); #endif // SRC_H // src.cpp ──────────────────────────────────────────────────────────────────────── #include "src.h" thread_local int Value; void set(int i) { State = i; } void prn() { std::cout << State << std::endl; } // main.cpp ─────────────────────────────────────────────────────────────────────── #include <src.h> #include <thread> #include <chrono> #include <ratio> #include <mutex> std::mutex Mutex; void func(int i) { set(i); std::this_thread::sleep_for( std::chrono::milliseconds(500 + ((std::rand() % 20) * 300)) ); std::lock_guard Guard(Mutex); std::cout << "(" << std::this_thread::get_id() << "): "; prn(); } int main() { std::srand(343452345); std::thread trd1(func, 10); std::thread trd2(func, 20); std::thread trd3(func, 30); trd1.join(); trd2.join(); trd3.join(); return 0; } Как и ожидалось, выводятся разные цифры, те, которыми инициализировали функции потоков (типа у каждого потока свое состояние либы): (4): 30 (2): 10 (3): 20 Ну, получается получается простая замена static на thred_local позволяет нам получить желаемое многопоточное использование? |
Сообщ.
#8
,
|
|
|
Цитата JoeUser @ Если считатьНу, получается получается простая замена static на thred_local позволяет нам получить желаемое многопоточное использование? 6804: the trdLocalVar is -1 now 6e1c: the trdLocalVar is -1 now 4060: the trdLocalVar is -1 now 3fe4: the trdLocalVar is -1 now 6e1c: the trdLocalVar is -1 now 3fe4: the trdLocalVar is -1 now 6804: the trdLocalVar is -1 now 3fe4: the trdLocalVar is -1 now 4060: the trdLocalVar is -1 now 3fe4: the trdLocalVar is -1 now 45cc: the trdLocalVar is 1 now 1110: the trdLocalVar is 2 now 5534: the trdLocalVar is 3 now 4b44: the trdLocalVar is -1 now 1110: the trdLocalVar is 2 now 4b44: the trdLocalVar is -1 now 45cc: the trdLocalVar is 1 now 4b44: the trdLocalVar is -1 now 5534: the trdLocalVar is 3 now 4b44: the trdLocalVar is -1 now |
Сообщ.
#9
,
|
|
|
Цитата Qraizer @ поменяла семантику этой ранее static переменной. В многпоточном исполнении - там не одна же переменная! Там одна копия переменной на один поток. Каждый поток юзает свою копию? И если "да", то что не так? |
Сообщ.
#10
,
|
|
|
Цитата Qraizer @ static обеспечивает локальность имени на основе статического деления программы на единицы трансляции, thread_local – на основе динамического разделения программы на потоки. |
Сообщ.
#11
,
|
|
|
Скорее всего все thread_local размещаются в отдельную виртуальную секцию. Виртуальную в том смысле, что линковщик собирает её воедино подобно секциям кода или данных, определяет смещения переменных, но не выделяет под секцию память (хотя наверно всё же выделяет, ведь есть ещё и основной поток со своими экземплярами этих же переменных).
Для новых потоков память распределяется динамически и инициализируется при их создании. При обращении к таким переменным обращение thread_local_var заменяется на что-то вроде get_thread_locals()->thread_local_var_as_struct_field По завершении потока память освобождается. |
Сообщ.
#12
,
|
|
|
amk, рассуждения вполне логичные! Вот если бы как-то, где-то найти реальный пруф ... Эх
|