Cách lấy id luồng số nguyên trong c ++ 11


84

c ++ 11 có khả năng nhận được id luồng hiện tại, nhưng nó không thể truyền sang kiểu số nguyên:

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

sản lượng: 139918771783456

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

lỗi: ép kiểu không hợp lệ từ kiểu 'std :: thread :: id' sang kiểu 'uint64_t' giống với các kiểu khác: kiểu truyền không hợp lệ từ kiểu 'std :: thread :: id' thành kiểu 'uint32_t'

Tôi thực sự không muốn thực hiện truyền con trỏ để lấy id chủ đề số nguyên. Có một số cách hợp lý (tiêu chuẩn vì tôi muốn nó di động) để làm điều đó?


13
Bạn cần nó là một số nguyên để làm gì? Nó được đảm bảo là không có ý nghĩa khi thực hiện số học thuộc bất kỳ loại nào trên nó và nó không có ý nghĩa bên ngoài ngữ cảnh của quá trình, vì vậy không cần phải tuần tự hóa nó ngoài việc gỡ lỗi (mà operator<<dường như xử lý tốt).
hmakholm còn lại Monica

4
một cái gì đó như thế này: 1024cores.net/home/lock-free-algorithms/false-sharing---false nhưng thay vì N = MAX_THREAD_COUNT tôi sẽ có một cái gì đó giống như N = 128 và làm thread_id% N
NoSenseEtAl

9
Nếu bạn thực sự muốn nó có thể di động, thì bạn cần phải chuẩn bị cho khả năng nó thread::idkhông được biểu diễn dưới dạng số nguyên. Trang bạn liên kết đến sử dụng một mảng, được lập chỉ mục theo ID chuỗi. Bạn đã xem xét sử dụng một map<thread::id, int>thay thế? Sau đó, bạn có thể sử dụng các toán tử quan hệ đã được xác định cho idlớp mà không cần thực hiện bất kỳ chuyển đổi nào. Tiêu chuẩn cũng xác định hash<thread::id>, vì vậy bạn cũng có thể sử dụng các vùng chứa không có thứ tự.
Rob Kennedy

3
@Rob rằng bản đồ sẽ yêu cầu mutexing :(
NoSenseEtAl,

1
@SwissFrank hay tôi nên nói CHF: PI vẫn còn ở đây, nhưng tôi nghĩ câu trả lời được chấp nhận là ok đối với tôi, tùy thuộc vào tôi để đảm bảo rằng các giá trị id biến là duy nhất trong suốt thời gian của chương trình.
NoSenseEtAl

Câu trả lời:


33

Giải pháp di động là chuyển các ID được tạo của riêng bạn vào chuỗi.

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

Các std::thread::idloại được sử dụng để so sánh chỉ, không cho số học (ví dụ như nó nói trên hộp: một định danh ). Ngay cả biểu diễn văn bản của nó được tạo ra operator<<cũng không xác định , vì vậy bạn không thể tin rằng nó là biểu diễn của một số.

Bạn cũng có thể sử dụng bản đồ các std::thread::idgiá trị cho id của riêng mình và chia sẻ bản đồ này (với đồng bộ hóa thích hợp) giữa các chuỗi, thay vì chuyển trực tiếp id.


1
Aha! Nhưng có một đại diện văn bản! Điều đó đủ tốt để con người trực quan tìm ra sự khác biệt giữa chúng, phải không?
Xunie

Giải pháp thread :: id (hoặc this_thread :: get_id ()) được đề cập ở đây là tốt nhất, vì nó không dành riêng cho lập trình viên. Xem câu trả lời dòng chuỗi của Mike bên dưới để biết biểu diễn chuỗi hoặc số nguyên.
Andrew

@Andrew Tôi đã giải quyết điều đó trong câu trả lời: "Ngay cả biểu diễn văn bản của nó được tạo ra bởi toán tử << là không xác định, vì vậy bạn không thể tin tưởng rằng nó là biểu diễn của một số". Có vẻ như một định nghĩa mờ ám của từ "tốt nhất" đã nằm trong tầm tay.
R. Martinho Fernandes

"tốt nhất" không liên quan đến biểu diễn chuỗi.
Andrew

1
Ngoài ra, tôi vừa thực hiện một điểm chuẩn với 10.000.000 lần lặp vì lợi ích của riêng mình và this_thread :: get_id () rất nhanh: pastebin.com/eLa3rKQE Chế độ gỡ lỗi mất 0,0000002543827 giây mỗi cuộc gọi và nhả ra mất 0,00000003652367 giây cho mỗi cuộc gọi đối với tôi. (Intel i5 2,60 GHz)
Andrew

85

Bạn chỉ cần làm

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

để có được một size_t.

Từ cppreference :

Sự đặc biệt của khuôn mẫu std::hashcho std::thread::idlớp cho phép người dùng có được các hàm băm của số nhận dạng của các luồng.


35
Tôi nghĩ điều này phải được std::hash<std::thread::id>()(std::this_thread::get_id()), phải không?
Barry

12
Liệu băm có được đảm bảo là duy nhất không? Có lẽ là không, đánh bại việc sử dụng nó như một định danh chủ đề duy nhất.
Michael Goldshteyn

2
Ví dụ như đã cho không hoạt động với ít nhất Clang 3.4 và libstdc ++ 4.8. Tuy nhiên, định dạng lại của Barry vẫn hoạt động.
Arto Bendiken

3
cảm ơn 888 cho câu trả lời. Trình biên dịch MS có luồng :: id :: hash () nhưng mã của Barry tuân thủ các tiêu chuẩn. Các băm có thể va chạm. Đó là chưa hữu ích để có một băm mỗi thread (với hy vọng một xác suất va chạm gần 0)
a.lasram

1
MSVC thực sự trả về một id luồng được băm trong trường hợp này. Bạn cũng có thể tạo riêng của bạn ...
rustyx

25

Một id khác (ý ​​tưởng? ^^) sẽ là sử dụng stringstreams:

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

Và sử dụng try catch nếu bạn không muốn có ngoại lệ trong trường hợp mọi thứ diễn ra không như mong muốn ...


2
Câu trả lời tốt. Điều này sẽ phục vụ mục đích nói chung.
iammilind

5
Điều này không phải là di động, vì không có gì đảm bảo rằng một std::thread::idbản in dưới dạng các ký tự tạo thành một số nguyên giống như cách mà không đảm bảo rằng id luồng được đại diện bên trong bằng một số nguyên.
blubberdiblub

1
@Nikos bất cứ khi nào một triển khai chọn rằng một số nguyên không đủ. Hoặc bất cứ khi nào nó cho là không phù hợp vì bất kỳ lý do nào khác. Vấn đề ở đây là khi đặc tả không chỉ định nó là một số nguyên (và không, nó chỉ có một số đảm bảo trừu tượng hơn), bạn không thể và không nên dựa vào nó là một số nguyên trong bất kỳ triển khai nào. Đơn giản chỉ cần sử dụng std::thread::idnhư kiểu thay vì một số số nguyên, đó là những gì nó tồn tại. Và đừng diễn giải lại cách biểu diễn chuỗi của nó dưới dạng các chữ số tạo nên một số. Coi nó là không rõ ràng hoặc như đầu ra gỡ lỗi / ghi nhật ký.
blubberdiblub

6

Một ý tưởng sẽ là sử dụng lưu trữ cục bộ của luồng để lưu trữ một biến - không quan trọng loại nào, miễn là nó tuân thủ các quy tắc lưu trữ cục bộ của luồng - sau đó sử dụng địa chỉ của biến đó làm "id luồng" của bạn. Rõ ràng là bất kỳ số thừa nào sẽ không có ý nghĩa, nhưng nó sẽ là một loại tích phân.

Đối với hậu thế: pthread_self()trả về mộtpid_t và là hậu tố. Đây là tính di động đối với một số định nghĩa về di động.

gettid(), gần như chắc chắn không phải là di động, nhưng nó trả lại giá trị thân thiện với GDB.


pthread_self()thực sự trả về a pthread_t, không rõ ràng (không giống như pid_t(trả về bởi gettid()), trong khi cũng dành riêng cho nền tảng, ít nhất là một số nguyên). Nhưng +1 cho bit đầu tiên, nó đã giải quyết được vấn đề của tôi!
Cameron

4

Tôi thực sự không biết điều này nhanh như thế nào, nhưng đây là giải pháp mà tôi quản lý được:

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;

Một lần nữa, tôi bắt đầu nghĩ rằng nhận được một con trỏ đến cấu trúc và truyền nó tới unsigned int hoặc uint64_t là câu trả lời ... 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 để ngăn chặn các vấn đề quái quỷ :) Viết lại rất dễ dàng so với việc săn lùng loại lỗi này. :)


3
Bạn không có gì đảm bảo rằng bạn sẽ không nhận được các giá trị trùng lặp với hashhàm, ít hơn nhiều nếu bạn% nó .
R. Martinho Fernandes

1
Bạn không thể nhận được sự đảm bảo đó với std::this_thread::get_id()! Nhưng bạn có thể không cần nó. Một vài luồng chia sẻ với nhau không tạo ra vấn đề lớn như mọi luồng chia sẻ với mọi luồng khác. Một cái gì đó như const size_t N_COUNTERS = 128; struct Counter { std::atomic<int> counter; char pad[CACHE_LINE_SIZE - sizeof(atomic<int>); } counters[N_COUNTERS];có lẽ là tốt. (Một nguyên tử hoặc spinlock để đồng bộ hóa rất nhẹ.)
Scott Lamb

@R. Martinho Fernandes Giống như tôi đã nói rằng tôi quan tâm đến giá trị int nên tôi có thể% nó, va chạm là ok nếu chúng hiếm, về cơ bản là những gì Scott nói.
NoSenseEtAl

1
Tôi thực sự đã thử điều này và tôi đã hoàn toàn sai - chỉ sử dụng atomic<int>thay vì intlà một sự chậm lại đáng kể ngay cả khi không có tranh chấp.
Scott Lamb

1
Bạn có thể thay thế static_assert bằng một cái gì đó giống như ideone.com/Q7Nh4 này (có thể dễ dàng điều chỉnh để thực thi yêu cầu kích thước chính xác nếu bạn muốn thay thế) để nó hoạt động linh hoạt hơn (ví dụ: lưu ý cách Ideone có id luồng 32 bit) .
R. Martinho Fernandes

4

thread::native_handle()trả về thread::native_handle_type, là một định dạng cho long unsigned int.

Nếu luồng được xây dựng mặc định, native_handle () trả về 0. Nếu có một luồng OS được đính kèm với nó, giá trị trả về là khác 0 (nó là pthread_t trên POSIX).


Nó được chỉ định std::thread::native_handle_typelà typedef cho long unsignedđâu? Trong 30.3.1 / 1, chúng ta chỉ có thể thấytypedef implementation-defined native_handle_type; // See 30.2.3
Ruslan

Một cách đơn giản nhưng đơn giản để phát hiện ra kiểu là tạo ra một lỗi biên dịch có chủ ý bằng cách gán thread :: native_handle () cho vd uint8_t. Sau đó, trình biên dịch sẽ phàn nàn về kiểu không khớp và cũng sẽ cho bạn biết kiểu là gì.
Alexey Polonsky

1
Đó là không di động vì nó phụ thuộc vào triển khai cụ thể.
Ruslan

Chà, ít nhất nếu triển khai cơ bản sử dụng pthread POSIX, thì có vẻ như native_handle () phải là pthread_t. Bây giờ, pthread_t là một kiểu con trỏ (typedef struct pthread * pthread_t). Vì vậy, có nghĩa là std :: thread :: native_handle_type là một kiểu số nguyên có khả năng chứa một con trỏ (ví dụ: size_t hoặc unsigned long).
Alexey Polonsky

3

Theo cách này, sẽ hoạt động:

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

Hãy nhớ bao gồm sstream thư viện


Đẹp, nhưng tại sao bạn lại cho rằng nó là một số nguyên? Nó có thể là hex hoặc bất cứ thứ gì khác.
gỉ

nếu bạn đang sử dụng std::stringstream, thì bạn có thể sử dụng nó operator >>để chuyển đổi thành int. Tôi thực sự muốn uint64_tlà loại idthay vì intnếu tôi chắc chắn rằng đó idlà tích phân.
aniliitb10

3

Một lý do chính để không sử dụng thread :: get_id () là nó không phải là duy nhất trong một chương trình / quy trình. Điều này là do id có thể được sử dụng lại cho luồng thứ hai, sau khi luồng đầu tiên kết thúc.

Đây có vẻ như là một tính năng khủng khiếp, nhưng nó là gì trong c ++ 11.


2

nó phụ thuộc vào những gì bạn muốn sử dụng thread_id cho; bạn có thể dùng:

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

Điều này sẽ tạo ra một id duy nhất mà bạn xử lý; nhưng có một hạn chế: nếu bạn khởi chạy một số phiên bản của cùng một quy trình và mỗi phiên bản trong số họ ghi id luồng của họ vào một tệp chung, tính duy nhất của thread_id sẽ không được đảm bảo; trên thực tế, rất có thể bạn sẽ có chồng chéo. Trong trường hợp này, bạn có thể làm như sau:

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

bây giờ bạn được đảm bảo id chủ đề duy nhất trên toàn hệ thống.


Quá tải operator<<có thể in bất cứ thứ gì , thật sai lầm khi cho rằng nó sẽ luôn in ra một số nguyên.
gỉyx

2

Một thay thế khác:

#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;
}

Mã được tạo cho hàm này bởi g ++ trong x86 64-bit chỉ là:

_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

Tức là một nhánh duy nhất không có bất kỳ đồng bộ hóa nào sẽ được dự đoán chính xác ngoại trừ lần đầu tiên bạn gọi hàm. Sau đó chỉ là một truy cập bộ nhớ duy nhất mà không cần đồng bộ hóa.


@NoSenseEtAl: Không chắc tôi hiểu câu hỏi của bạn ... thread_localđã mô tả thời lượng lưu trữ cho tid. Đối staticvới thread_counterlà vì bạn không muốn để lộ nó ra bên ngoài đơn vị biên dịch này.
6502

Loại kỳ lạ này chỉ định ID chuỗi theo thứ tự bạn truy vấn ID chuỗi. (Bản thân tôi đã làm một việc RẤT giống và tôi chưa bao giờ thích sự kỳ lạ này.) Nó cũng chỉ định từ 0, điều này không bình thường. (Ví dụ: GDB báo cáo ID chuỗi bắt đầu từ 1)
Swiss Frank

1
@SwissFrank: nó chỉ là một con số và bạn không nên đọc quá nhiều về giá trị trả về: không có cách nào hợp pháp để biết rằng nó đã được chỉ định khi bạn truy vấn nó :-). Về thực tế đó 0là ID hợp lệ là một điểm tốt và có thể được sửa bằng cách sử dụng preincrement. Tôi sẽ thay đổi câu trả lời để làm điều đó.
6502

1

Có thể giải pháp này hữu ích cho ai đó. Gọi nó là lần đầu tiên tôi main(). Cảnh báo: namesphát triển vô thời hạn.

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];
}

không sử dụng stringstream, nó chậm, hãy sử dụng std :: to_string
NoSenseEtAl
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.