Как получить целочисленный идентификатор потока в c ++ 11

88

c ++ 11 имеет возможность получить текущий идентификатор потока, но он не может быть приведен к целочисленному типу:

cout<<std::this_thread::get_id()<<endl;

вывод: 139918771783456

cout<<(uint64_t)std::this_thread::get_id()<<endl;

ошибка: недопустимое приведение из типа 'std :: thread :: id' к типу 'uint64_t', то же самое для других типов: недопустимое приведение из типа 'std :: thread :: id' для типа 'uint32_t'

Я действительно не хочу выполнять приведение указателя, чтобы получить целочисленный идентификатор потока. Есть ли какой-нибудь разумный способ (стандартный, потому что я хочу, чтобы он был портативным) сделать это?

NoSenseEtAl
источник
13
Зачем нужно, чтобы это было целое число? Гарантируется, что выполнять какие-либо арифметические операции над ним не имеет смысла, и это не имеет смысла вне контекста процесса, поэтому не должно быть необходимости сериализовать его, кроме как для отладки (которая, operator<<похоже, справляется нормально).
Хмахольм ушел из-за Моники
4
что-то вроде этого: 1024cores.net/home/lock-free-algorithms/false-sharing---false, но вместо N = MAX_THREAD_COUNT у меня будет что-то вроде N = 128 и сделать thread_id% N
NoSenseEtAl
9
Если вы действительно хотите, чтобы он был переносимым, вам нужно быть готовым к тому, что оно thread::idвообще не будет представлено в виде целого числа. Страница, на которую вы ссылаетесь, использует массив, индексированный по идентификатору потока. Вы не думали использовать map<thread::id, int>вместо этого? Затем вы можете использовать операторы отношения, уже определенные для idкласса, без каких-либо преобразований. Стандарт также определяет hash<thread::id>, поэтому вы также можете использовать неупорядоченные контейнеры.
Роб Кеннеди,
3
@Rob, эта карта потребует мьютекса :(
NoSenseEtAl
1
@SwissFrank, или мне следует сказать CHF: PI все еще существует, но я думаю, что принятый ответ подходит для меня, я должен убедиться, что значения идентификаторов переменных уникальны для продолжительности программы.
NoSenseEtAl

Ответы:

35

Переносимое решение - передать ваши собственные сгенерированные идентификаторы в поток.

int id = 0;
for(auto& work_item : all_work) {
    std::async(std::launch::async, [id,&work_item]{ work_item(id); });
    ++id;
}

std::thread::idТип должен использоваться для сравнения, а не для арифметической (то есть , как он говорит на баллончике: идентификатор ). Даже его текстовое представление, созданное с помощью, operator<<не определено , поэтому вы не можете полагаться на то, что это представление числа.

Вы также можете использовать карту std::thread::idзначений для своего собственного идентификатора и совместно использовать эту карту (с надлежащей синхронизацией) между потоками вместо передачи идентификатора напрямую.

Р. Мартиньо Фернандес
источник
1
Ага! Но это текстовое представление! Этого достаточно, чтобы люди могли визуально различить их, не так ли?
Xunie
Упомянутое здесь решение thread :: id (или this_thread :: get_id ()) является лучшим, потому что оно не зависит от программиста. См. Ответ Майка на строковый поток ниже, чтобы получить строковое или целочисленное представление.
Эндрю
@Andrew Я обратился к этому в ответе: «Даже его текстовое представление, созданное оператором <<, не определено, поэтому вы не можете полагаться на то, что оно является представлением числа». Похоже, под рукой туманное определение слова «лучший».
Р. Мартиньо Фернандес,
«лучший» не имел отношения к строковому представлению.
Эндрю
1
Кроме того, я только что провел тест с 10 000 000 итераций ради себя, и this_thread :: get_id () работает ужасно быстро: pastebin.com/eLa3rKQE Режим отладки занимает 0,0000002543827 секунд на вызов, а Release занимает 0,00000003652367 секунд для меня. (Intel i5 2,60 ГГц)
Эндрю
89

Вам просто нужно сделать

std::hash<std::thread::id>{}(std::this_thread::get_id())

чтобы получить size_t.

Из cppreference :

Специализация шаблона std::hashдля std::thread::idкласса позволяет пользователям получать хэши идентификаторов потоков.

888
источник
35
Я думаю, так и должно быть std::hash<std::thread::id>()(std::this_thread::get_id()), не так ли?
Барри
12
Будет ли хеш гарантированно уникальным? Скорее всего, нет, так как он не используется в качестве уникального идентификатора потока.
Майкл Гольдштейн
2
Данный пример не работает по крайней мере с Clang 3.4 и libstdc ++ 4.8. Однако переформулировка Барри работает.
Arto Bendiken
3
спасибо 888 за ответ. В компиляторе MS есть thread :: id :: hash (), но код Барри соответствует стандартам. Хэши могут конфликтовать. Еще полезно иметь хеш для каждого потока (с вероятностью коллизии, близкой к 0)
a.lasram
1
В этом случае MSVC фактически возвращает хешированный идентификатор потока. С таким же успехом ты можешь создать свой собственный ...
rustyx 03
26

Другой идентификатор (идея? ^^) - использовать строковые потоки:

std::stringstream ss;
ss << std::this_thread::get_id();
uint64_t id = std::stoull(ss.str());

И используйте try catch, если вы не хотите исключения в случае, если что-то пойдет не так ...

Майк
источник
2
Хороший ответ. Это послужило бы цели в целом.
iammilind
5
Это не переносимо, поскольку нет гарантии, что a будет std::thread::idпечататься как символы, составляющие целое число, во многом так же, как не гарантируется, что идентификатор потока внутренне представлен целым числом.
blubberdiblub
1
@Nikos всякий раз, когда реализация решает, что целого числа недостаточно. Или когда сочтет это неприемлемым по какой-либо другой причине. Дело здесь в том, что когда спецификация не определяет его как целое число (а это не так, у него просто есть несколько более абстрактных гарантий), вы не можете и не должны полагаться на то, что это целое число в любой реализации. Просто используйте std::thread::idкак тип вместо целого числа, для чего он существует. И не интерпретируйте его строковое представление заново как цифры, составляющие число. Рассматривайте его как непрозрачный или как вывод отладки / ведения журнала.
blubberdiblub
6

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

Для потомков: pthread_self()возвращает a pid_tи is posix. Это переносимо для некоторого определения переносимости.

gettid(), почти наверняка не переносится, но возвращает дружественное к GDB значение.

Tgoodhart
источник
pthread_self()фактически возвращает a pthread_t, который является непрозрачным (в отличие от pid_t(возвращенного gettid()), который, хотя и зависит от платформы, по крайней мере, является целым числом). Но +1 для первого бита решил мою проблему!
Кэмерон
4

Я действительно не знаю, насколько это быстро, но это решение, которое мне удалось предположить:

const size_t N_MUTEXES=128;//UINT_MAX,not 128  for answer to my original question
hash<std::thread::id> h;
cout<<h(std::this_thread::get_id())%N_MUTEXES<<endl;

Опять же, я начинаю думать, что получение указателя на структуру и приведение его к unsigned int или uint64_t - это ответ ... EDIT:

uint64_t get_thread_id()
{
    static_assert(sizeof(std::thread::id)==sizeof(uint64_t),"this function only works if size of thead::id is equal to the size of uint_64");
    auto id=std::this_thread::get_id();
    uint64_t* ptr=(uint64_t*) &id;
    return (*ptr);
}
int main()
{
    cout<<std::this_thread::get_id()<<"  "<<get_thread_id()<<endl;
}

static_assert для предотвращения адских проблем :) Переписать проще, чем выслеживать такого рода ошибки. :)

NoSenseEtAl
источник
3
У вас нет гарантий, что вы не получите повторяющихся значений с hashфункцией, тем более, если вы% ее .
R. Martinho Fernandes
1
Вы не можете получить эту гарантию std::this_thread::get_id()! Но, вероятно, вам это не нужно. Пара потоков, совместно используемых друг с другом, не создает такой серьезной проблемы, как каждый поток, совместно используемый с каждым другим потоком. Что-то вроде const size_t N_COUNTERS = 128; struct Counter { std::atomic<int> counter; char pad[CACHE_LINE_SIZE - sizeof(atomic<int>); } counters[N_COUNTERS];, наверное, нормально. (Атомарная или спин-блокировка для очень легкой синхронизации.)
Скотт Лэмб
@Р. Мартиньо Фернандес: Как я уже сказал, меня интересует значение int, поэтому я могу его%, коллизии допустимы, если они редки, в основном то, что сказал Скотт.
NoSenseEtAl
1
Я на самом деле пробовал это и был совершенно неправ - простое использование atomic<int>вместо int- это резкое замедление даже без разногласий.
Скотт Лэмб,
1
Вы можете заменить static_assert на что-то вроде этого ideone.com/Q7Nh4 (легко настраиваемый, чтобы обеспечить соблюдение требований к точному размеру, если вы этого хотите), чтобы он работал более переносимо (обратите внимание, как ideone имеет 32-битный идентификатор потока, например) .
R. Martinho Fernandes
4

thread::native_handle()возвращает thread::native_handle_type, который является typedef для long unsigned int.

Если поток создан по умолчанию, native_handle () возвращает 0. Если к нему подключен поток ОС, возвращаемое значение не равно нулю (это pthread_t в POSIX).

Алексей Полонский
источник
Где указано, что std::thread::native_handle_typeэто typedef для long unsigned? В 30.3.1 / 1 мы можем видеть толькоtypedef implementation-defined native_handle_type; // See 30.2.3
Руслан
Тупой, но простой способ определить тип - это создать преднамеренную ошибку компиляции, присвоив thread :: native_handle (), например, uint8_t. Затем компилятор будет жаловаться на несоответствие типов, а также сообщит вам, что это за тип.
Алексей Полонский
1
Ну, это непереносимо, поскольку зависит от конкретной реализации.
Руслан
Ну, по крайней мере, если базовая реализация использует POSIX pthread, кажется, что native_handle () должен быть pthread_t. Теперь pthread_t - это тип указателя (typedef struct pthread * pthread_t). Таким образом, имеет смысл, что std :: thread :: native_handle_type - это целочисленный тип, способный содержать указатель (например, size_t или unsigned long).
Алексей Полонский
3

Таким образом должны работать:

std::stringstream ss;
ss << std::this_thread::get_id();
int id = std::stoi(ss.str());

Не забудьте включить библиотеку sstream

Федерико Риццо
источник
Хорошо, но почему вы решили, что это целое число? Это может быть шестнадцатеричный код или что-то еще.
rustyx 03
если вы используете std::stringstream, вы можете использовать его operator >>для преобразования в int. На самом деле я бы предпочел uint64_tкак тип, idа не intесли я уверен, что idэто целое.
aniliitb10 07
3

Основная причина не использовать thread :: get_id () заключается в том, что он не уникален для одной программы / процесса. Это потому, что идентификатор может быть повторно использован для второго потока после завершения первого потока.

Это кажется ужасной функцией, но это то, что есть в С ++ 11.

Midjji
источник
2

это зависит от того, для чего вы хотите использовать thread_id; вы можете использовать:

std::stringstream ss;
ss << std::this_thread::get_id();
uint64_t id = std::stoull(ss.str());

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

#include <sys/time.h>
timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
uint64_t id = (ts.tv_sec % 1000000000) * 1000000000 + ts.tv_nsec;

теперь вам гарантированы уникальные идентификаторы потоков во всей системе.

Пандрей
источник
Перегруженный operator<<может печатать что угодно , ошибочно полагать, что он всегда будет печатать целое число.
rustyx 03
2

Другая альтернатива:

#include <atomic>

static std::atomic<unsigned long long> thread_counter;

unsigned long long thread_id() {
    thread_local unsigned long long tid = ++thread_counter;
    return tid;
}

Сгенерированный код для этой функции с помощью g ++ в 64-разрядной версии x86 выглядит так:

_Z9thread_idv:
        cmp     BYTE PTR fs:_ZGVZ9thread_idvE3tid@tpoff, 0
        je      .L2
        mov     rax, QWORD PTR fs:_ZZ9thread_idvE3tid@tpoff
        ret
.L2:
        mov     eax, 1
        lock xadd       QWORD PTR _ZL14thread_counter[rip], rax
        mov     BYTE PTR fs:_ZGVZ9thread_idvE3tid@tpoff, 1
        mov     QWORD PTR fs:_ZZ9thread_idvE3tid@tpoff, rax
        ret
_ZGVZ9thread_idvE3tid:
        .zero   8
_ZZ9thread_idvE3tid:
        .zero   8

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

6502
источник
@NoSenseEtAl: Не уверен, что я понимаю ваш вопрос ... thread_localуже описан срок хранения tid. staticДля thread_counterпотому , что вы не хотите , чтобы выставить его за пределами этого модуля компиляции.
6502
Этот вид странным образом назначает идентификаторы потока в том порядке, в котором вы запрашиваете идентификатор потока. (Я сам делал нечто ОЧЕНЬ похожее, и мне никогда не нравилась эта странность.) Он также присваивается с нуля, что необычно. (Например, GDB сообщает идентификаторы потоков, начинающиеся с 1.)
Швейцарский Франк,
1
@SwissFrank: это просто число, и вы не должны слишком много читать в возвращаемом значении: нет законного способа узнать, что оно было присвоено, когда вы его запрашивали :-). О том, что 0это действительный идентификатор, это хороший момент, и его можно исправить с помощью преинкремента. Для этого я изменю ответ.
6502,
1

Может быть, это решение кому-то поможет. Назовите это впервые им main(). Предупреждение: namesнеограниченно растёт.

std::string currentThreadName(){
    static std::unordered_map<std::thread::id,std::string> names;
    static std::mutex mtx;

    std::unique_lock<std::mutex> lock(mtx);

    auto id = std::this_thread::get_id();

    if(names.empty()){
        names[id] = "Thread-main";
    } else if(names.find(id) == names.end()){
        std::stringstream stream;
        stream << "Thread-" << names.size();
        names[id] = stream.str();
    }

    return names[id];
}
гэ
источник
не используйте stringstream, он медленный, используйте std :: to_string
NoSenseEtAl