Доступ к локальному потоку из другого потока - PullRequest
17 голосов
/ 15 марта 2011

Как я могу читать / записывать локальную переменную потока из другого потока?То есть в потоке AI хотел бы получить доступ к переменной в области локального хранилища потока B.Я знаю идентификатор другого потока.

Переменная объявлена ​​как __thread в GCC.Целевой платформой является Linux, но независимость может быть хорошей (однако для GCC все в порядке).

Отсутствие ловушки для запуска потока Я просто не могу отследить это значение в начале каждого потока.Все потоки должны отслеживаться таким образом (не только специально запущенные).

Обертка более высокого уровня, например boost thread_local_storage или использование ключей pthread, не подходит.Мне нужна производительность с использованием истинной __thread локальной переменной.


ПЕРВЫЙ ОТВЕТ НЕПРАВИЛЬНЫЙ : Нельзя использовать глобальные переменные для того, что я хочу сделать.Каждый поток должен иметь свою собственную копию переменной.Кроме того, эти переменные должны быть __thread переменными по соображениям производительности (одинаково эффективное решение также было бы хорошо, но я не знаю ни одного).Я также не контролирую точки входа потоков, поэтому у этих потоков нет возможности зарегистрировать любой тип структуры.


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

Ответы [ 3 ]

15 голосов
/ 15 марта 2011

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

Важное уточнение!

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

Вы, конечно, должны будете обеспечить синхронизацию, но, поскольку вы хотите выставить значение, измененное в потоке A, в поток B, это не поможет.

Обновление:

Документация GCC по __thread гласит:

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

Поэтому, если вы настаиваете на том, чтобы идти по этому пути, я представляю, что можно получить адрес локальной переменной потока из потока, которому он принадлежит, сразу после того, как поток будет создан. Затем вы можете сохранить указатель на эту ячейку памяти на карте (идентификатор потока => указатель) и позволить другим потокам обращаться к переменной таким образом. Это предполагает, что вы владеете кодом для порожденного потока.

Если вы действительно любите приключения, попробуйте поискать информацию по ___tls_get_addr (начиная с этого PDF , на который ссылаются вышеупомянутые документы GCC). Но этот подход настолько сильно зависит от компилятора и платформы и настолько не хватает документации, что он должен вызывать срабатывание сигналов тревоги у всех.

3 голосов
/ 28 апреля 2016

Я ищу то же самое. Как я вижу, никто не ответил на ваш вопрос, после того, как я вошел в Интернет всеми способами, я пришел к следующей информации: предположим, что для компиляции gcc на Linux (Ubuntu) и с использованием -m64, регистр сегмента gs содержит значение 0. Скрытая часть сегмента (с линейным адресом) указывает на конкретную локальную область потока. Эта область содержит по этому адресу адрес этого адреса (64 бита). По нижним адресам хранятся все локальные переменные потока. Этот адрес native_handle(). Таким образом, чтобы получить доступ к локальным данным потоков, вы должны сделать это через этот указатель.

Другими словами: (char*)&variable-(char*)myThread.native_handle()+(char*)theOtherThread.native_handle()

Код, демонстрирующий вышесказанное, предполагая, что g ++, linux, pthreads:

#include <iostream>
#include <thread>
#include <sstream>

thread_local int B=0x11111111,A=0x22222222;

bool shouldContinue=false;

void code(){
    while(!shouldContinue);
    std::stringstream ss;
    ss<<" A:"<<A<<" B:"<<B<<std::endl;
    std::cout<<ss.str();
}

//#define ot(th,variable) 
//(*( (char*)&variable-(char*)(pthread_self())+(char*)(th.native_handle()) ))

int& ot(std::thread& th,int& v){
    auto p=pthread_self();
    intptr_t d=(intptr_t)&v-(intptr_t)p;
    return *(int*)((char*)th.native_handle()+d);
}

int main(int argc, char **argv)
{       

        std::thread th1(code),th2(code),th3(code),th4(code);

        ot(th1,A)=100;ot(th1,B)=110;
        ot(th2,A)=200;ot(th2,B)=210;
        ot(th3,A)=300;ot(th3,B)=310;
        ot(th4,A)=400;ot(th4,B)=410;

        shouldContinue=true;

        th1.join();
        th2.join();
        th3.join();
        th4.join();

    return 0;
}
2 голосов
/ 20 июня 2014

Я, к сожалению, так и не смог найти способ сделать это.

Без какого-либо хука инициализации потока, похоже, просто нет способа добраться до этого указателя (если не считать хаков ASM, которые будут зависеть от платформы).

...