The original question,对如何进行非线程安全版本有了很好的答案。
以下是我尝试稍微修改以开始工作的代码:
#include <stdio.h>
#include <functional>
#include <thread>
void register_with_library(int (*func)(int *k, int *e)) {
int x = 0, y = 1;
int o = func(&x, &y);
}
typedef int (*callback_t)(int*,int*);
class A {
template <typename T>
struct Callback;
template <typename Ret, typename... Params>
struct Callback<Ret(Params...)> {
template <typename... Args>
thread_local static Ret callback(Args... args) {
func(args...);
}
thread_local static std::function<Ret(Params...)> func;
};
public:
A();
~A();
int e(int *k, int *j);
private:
callback_t func;
};
template <typename Ret, typename... Params>
thread_local std::function<Ret(Params...)> A::Callback<Ret(Params...)>::func;
A::A() {
Callback<int(int*,int*)>::func = std::bind(&A::e, this, std::placeholders::_1, std::placeholders::_2);
printf("1. C callback function ptr %p, C++ template function ptr %p Object ptr %p \n",func, Callback<int(int*,int*)>::func, this) ;
func = static_cast<callback_t>(Callback<int(int*,int*)>::callback);
printf("2. C callback function ptr %p\n",func) ;
register_with_library(func);
}
int A::e(int *k, int *j) {
return *k - *j;
}
A::~A() { }
int main() {
std::thread t1 = std::thread { [](){ A a;}};
std::thread t2 = std::thread { [](){ A a;}};
t1.join();
t2.join();
}
结果是
function ptr 0x400eef
function ptr 0x400eef
考虑到我有多个线程创建不同的对象,如何正确地为每个新对象创建新的回调?
编辑:
正如e.jahandar所建议的,使用thread_local可以部分解决问题(仅当每个线程创建了1个对象时)。由于这个原因,Callback<int(int*,int*)>::func
是基于线程分配的。虽然问题仍存在于Callback<int(int*,int*)>::callback
。
没有thread_local:
1. C callback function ptr 0x403148, C++ template function ptr 0x609180 Object ptr 0x7ff9ac9f3e60
2. C callback function ptr 0x403673
1. C callback function ptr 0x4031a6, C++ template function ptr 0x609180 Object ptr 0x7ff9ad1f4e60
2. C callback function ptr 0x403673
使用thread_local:
1. C callback function ptr 0x403230, C++ template function ptr 0x7fc1ecc756d0 Object ptr 0x7fc1ecc74e20
2. C callback function ptr 0x403701
1. C callback function ptr 0x4031d2, C++ template function ptr 0x7fc1ec4746d0 Object ptr 0x7fc1ec473e20
2. C callback function ptr 0x403701
答案 0 :(得分:1)
如果每个线程只需要一个特定对象的实例,则可以使用带有__thread存储类的对象指针的全局变量,__ thread读取全局变量对该线程的唯一性。
使用带静态成员的单调类进行回调是另一种解决方案,就像之前的解决方案一样,您可以使用__thread为每个线程分离monothonic类实例。
另外请注意,__thread不是标准的东西
修改强>
这是一个例子
class.h
class someClass{
private:
someMethod(){ ... }
}
class.cpp
__thread void * objectPointer;
void initialize(){
someClass * classPtr = new someClass();
objectPointer = (void *) classPtr;
}
void * callbackFunction(void * args){
someClass * obj = objectPointer;
obj->someMethod();
}
答案 1 :(得分:0)
使用C语言调用C ++函数的唯一符合标准的方法是使用C链接声明它。
extern "C" int my_callback_wrapper (A*, int*, int*);
my_callback
既不是模板,也不是成员函数(甚至是静态的)。
除此之外的任何事情都是未定义的行为。
为了符合标准,您必须使用单独的回调包装器手动包装每个单独的成员函数。此解决方案自然是线程安全的,因为它不使用任何全局或静态数据。
当然,它需要C代码获取A对象的指针,并在正确的时刻将右指针传递给回调。从C库隐藏A
的实例相当于在一些静态/全局存储中存储A,这意味着每个回调只能有一个。如果您使用的是C ++ 11,则还可以指定thread_local存储并为每个线程创建一个对象。
extern "C" int my_callback_wrapper (int* x, int* y);
thread_local A* aptr = nullptr;
thread_local int (A::*fptr)(int*, int*) = nullptr;
void register_cxx_callback (A* a, int (A::*f)(int*, int*))
{
if (aptr != nullptr || fptr != nullptr)
fatal_error ("Trying to overwrite the callback!");
aptr = a;
fptr = f;
register_with_library(my_callback_wrapper);
}
extern "C" int my_callback_wrapper (int* x, int* y)
{
if (aptr == nullptr || fptr == nullptr)
fatal_error ("Callback is called but the object is not registered!");
printf ("aptr is %p\n", (void*)aptr);
return (aptr->*fptr)(x, y);
}
一个完整的工作示例是here。
只要C库没有尝试在线程之间传递已注册的回调,此解决方案也是线程安全的。据我所知,它也符合标准。
没有模板,因为没有C链接模板。可以将静态/线程局部数据包含在一个类中,但是我不会在只有静态数据的类中看到很多点。
无法为每个线程移植注册任意数量的对象。最后,你必须提供一个C函数指针,并且C函数不能可移植地隐藏任意数据(IOW你不能在符合标准的C中构建闭包)。如果您对非便携式解决方案感兴趣,可以使用library that does that。