我正在嘗試使用 pthreads 為 linux 撰寫自己的 c 包裝器類。“Thread”類應該獲得一個通用的 lambda 以在不同的執行緒中運行,并抽象出為此所需的 pthread 呼叫。
如果 lambda 不捕獲任何東西,這就可以正常作業,但是一旦它們捕獲了一些共享變數,行為似乎就會變得不確定,這取決于兩個執行緒的模板型別是否相同。如果兩個 Thread 物件 caputre 相同的型別(int 和 int*),它似乎(可能是意外地)作業正常,但是一旦我傳遞(例如在我的例子中)一個整數 A'aka。stackInt ' 和一個 int ptr B 'aka。heapInt ' 到執行緒 1,只有 int ptr B 到執行緒 2 我在訪問 int ptr B 時在執行緒 2 中遇到段錯誤。
我知道這一定與每個執行緒都有自己的堆疊段副本這一事實有關,但是我無法理解這如何干擾通過參考并呼叫它們來捕獲變數的 colsures。int ptr B 的值不應該指向堆疊上它的每個副本中的相同地址嗎?地址怎么弄亂了?我真的無法理解這里的確切問題是什么......
有誰可以幫我離開這里嗎?先感謝您。
這是完整的示例代碼:
“執行緒”類
// thread.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
// ******************************* //
// THREAD CLASS //
// ******************************* //
template <typename C>
class Thread
{
private:
C &m_closure;
pthread_t m_thread;
public:
Thread<C>(C &&closure)
: m_closure(closure),
m_thread()
{}
void start()
{
pthread_create(&m_thread, NULL, &Thread::threadFunction, (void *)this);
}
void join()
{
pthread_join(m_thread, NULL);
}
private:
void callbackOnInstance()
{
m_closure();
}
static void * threadFunction(void *);
};
template <typename C>
void * Thread<C>::threadFunction(void *caller)
{
Thread<C> *callerObject = (Thread<C> *)caller;
callerObject->callbackOnInstance();
return nullptr;
}
主要()/測驗
// main.cpp
// ******************************* //
// TESTING //
// ******************************* //
#define SLEEP_SEC(_sec) usleep((long)(1000 * 1000 * (_sec)))
#include "thread.h"
#include <iostream>
#include <string>
int main(int argc, char **argv)
{
int stackInt = 0;
int *heapInt = new int(0);
// every second each thread increments them, 0.5 s apart from each other
Thread thread1([&]()
{
while(true)
{
SLEEP_SEC(1);
std::cout << "thread #1:" << std::endl;
stackInt = 1;
std::cout << "stack int: " << stackInt << " [" << &stackInt << "]" << std::endl;
*heapInt = 1;
std::cout << "heap int: " << *heapInt << " [" << heapInt << "]" << std::endl;
}
});
thread1.start();
Thread thread2([&]()
{
SLEEP_SEC(0.5);
while(true)
{
SLEEP_SEC(1);
std::cout << "thread #2:" << std::endl;
// if stackInt doesn't get referenced ...
//stackInt = 1;
//std::cout << "stack int: " << stackInt << " [" << &stackInt << "]" << std::endl;
// ... i get a segfault here
*heapInt = 1;
std::cout << "heap int: " << *heapInt << " [" << heapInt << "]" << std::endl;
}
});
thread2.start();
thread1.join();
thread2.join();
}
uj5u.com熱心網友回復:
你有未定義的行為,因為 lambda 物件實際上在建構式Thread完成后立即被銷毀。要看到這一點,您可以傳遞一個在解構式中列印訊息的物件,而不是 lambda:
struct ThreadFunctor
{
int& stackInt;
int* heapInt;
ThreadFunctor(int& si, int* hi)
: stackInt(si),
heapInt(hi)
{
std::cout << "ThreadFunctor created: " << this << '\n';
}
~ThreadFunctor()
{
std::cout << "ThreadFunctor destroyed: " << this << '\n';
}
void operator()() const
{
using namespace std::chrono_literals;
while (true)
{
std::this_thread::sleep_for(1s);
std::cout << "thread #1:" << std::endl;
stackInt = 1;
std::cout << "stack int: " << stackInt << " [" << &stackInt << "]" << std::endl;
*heapInt = 1;
std::cout << "heap int: " << *heapInt << " [" << heapInt << "]" << std::endl;
}
}
};
Thread thread1(ThreadFunctor{stackInt, heapInt});
std::cout << "before start\n";
thread1.start();
每個符合標準的 C 編譯器(模地址)都保證有以下輸出:
ThreadFunctor destroyed: 0000006E3ED6F6F0
before start
...
此外,連接操作僅在后臺執行緒上的操作完成后才完成,因此由于無限回圈,您的程式不會終止。您需要某種方式來通知后臺執行緒實際回傳而不是永遠繼續。
請注意,標準庫已經包含您要在此處實作的確切邏輯:std::thread或者std::jthread用于使用內置方式通知后臺執行緒終止請求的實作。
int main()
{
using namespace std::chrono_literals;
int stackInt = 0;
int* heapInt = new int(0);
// every second each thread increments them, 0.5 s apart from each other
std::jthread thread1{ [=](std::stop_token stopToken, int& stackInt)
{
using namespace std::chrono_literals;
while (!stopToken.stop_requested())
{
std::this_thread::sleep_for(1s);
std::cout << "thread #1:" << std::endl;
stackInt = 1;
std::cout << "stack int: " << stackInt << " [" << &stackInt << "]" << std::endl;
*heapInt = 1;
std::cout << "heap int: " << *heapInt << " [" << heapInt << "]" << std::endl;
}
}, std::ref(stackInt) }; // thread started immediately
std::this_thread::sleep_for(10s);
thread1.request_stop();
thread1.join();
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/537513.html
標籤:C 模板线程未定义的行为
