2017-11-09 6 views
1

이디엄을 thread_local과 함께 사용할 수 있습니까?thread_local이 포함 된 C++ Schwarz 카운터

class ThisThread{ 
    JNIEnv* jni_env{nullptr}; 
public: 
    JNIEnv* getEnv(){ 
     if (!jni_env){ 
      // Attach thread 
      java_vm->GetEnv((void**)&jni_env, JNI_VERSION); 
      java_vm->AttachCurrentThread(&jni_env, NULL); 
     } 

     return jni_env; 
    } 

    ~ThisThread(){ 
     if (!jni_env) return; 
     // Deattach thread 
     java_vm->DetachCurrentThread(); 
    } 
}; 

static thread_local ThisThread this_thread; 

먼저 구축 할하려면 각 스레드의 마지막 파괴 :

나는이 (자바 JNI 스레드에 대한 도우미)가 필요합니다 (I가 thread_local 모든 static을 대체 가정). 다른 정적 또는 thread_local 객체의 소멸자/생성자에서 this_thread->getEnv()을 호출 할 수 있습니다.

UPDATE

https://stackoverflow.com/a/30200992 - 여기가 표준 thread_local 소멸자 정적 전에이라고 말한다, 나는 이후로이 일을해야합니다.

+0

ThisThread에 대한 참조가 thread_local이기도합니다. –

+0

@RichardHodges 무엇을 의미합니까? – tower120

+0

초, 데모를 노크합니다 –

답변

1

정상적인 방법으로 schwartz 카운터를 구현하는 것이 가장 좋지만 ThisThread 클래스는 thread_local 정적 인 Impl으로 구현하는 것이 가장 좋습니다. 출력을

전체 예제 :

// header file 
#include <memory> 
#include <mutex> 
#include <iostream> 
#include <thread> 

std::mutex emit_mutex; 

template<class...Ts> 
void emit(Ts&&...ts) 
{ 
    auto action = [](auto&&x) { std::cout << x; }; 
    auto lock = std::unique_lock<std::mutex>(emit_mutex); 

    using expand = int[]; 
    expand{ 0, 
     (action(std::forward<Ts>(ts)), 0)... 
    }; 
} 


struct ThisThread 
{ 
    struct Impl 
    { 
     Impl() 
     { 
      emit("ThisThread created on thread ", std::this_thread::get_id(), '\n'); 
     } 
     ~Impl() 
     { 
      emit("ThisThread destroyed on thread ", std::this_thread::get_id(), '\n'); 
     } 
     void foo() 
     { 
      emit("foo on thread ", std::this_thread::get_id(), '\n'); 
     } 
    }; 

    decltype(auto) foo() { return get_impl().foo(); } 

private: 
    static Impl& get_impl() { return impl_; } 
    static thread_local Impl impl_; 
}; 

struct ThisThreadInit 
{ 

    ThisThreadInit(); 
    ~ThisThreadInit(); 

    static int initialised; 
}; 

extern ThisThread& thisThread; 
static ThisThreadInit thisThreadInit; 



// cppfile 

static std::aligned_storage_t<sizeof(ThisThread), alignof(ThisThread)> storage; 
ThisThread& thisThread = *reinterpret_cast<ThisThread*>(std::addressof(storage)); 
int ThisThreadInit::initialised; 
thread_local ThisThread::Impl ThisThread::impl_; 

ThisThreadInit::ThisThreadInit() 
{ 
    if (0 == initialised++) 
    { 
     new (std::addressof(storage)) ThisThread();  
    } 
} 

ThisThreadInit::~ThisThreadInit() 
{ 
    if (0 == --initialised) 
    { 
     thisThread.~ThisThread(); 
    } 
} 


// now use the object 

#include <thread> 

int main() 
{ 
    thisThread.foo(); 

    auto t = std::thread([]{ thisThread.foo(); }); 
    t.join(); 
} 

예 출력 :

ThisThread created on thread 140475785611072 
foo on thread 140475785611072 
ThisThread created on thread 140475768067840 
foo on thread 140475768067840 
ThisThread destroyed on thread 140475768067840 
ThisThread destroyed on thread 140475785611072 
+0

메인에서 https://pastebin.com/tNg7TisH – tower120

+0

@ tower120에서 작동하지 않습니다. 왜 작동하지 않는 걸까요? 예상대로 생성자와 소멸자가 호출되는 것을 봅니다. –

+0

죄송합니다. mingw-w64 버그 또는 내 툴체인의 다른 오작동으로 보입니다. https://stackoverflow.com/questions/47226542/c-why-static-thread-local-object-constructed-twice. 나는 VS 2017을 시도했는데, 그 테스트에서 예상대로 작동하는 것 같습니다. 그러나...뭔가 아직도 나를 혼란스럽게합니다 - 우리는 실제로 thread_locals를 계산하지 않습니다 ... – tower120

0

thread_local static에 대한 슈왈츠는 카운터를 만드는 방법을 대답하지 않는다 (그래서 답변으로이 동의하지 않습니다). 그러나 결국이 플랫폼에 종속적 인 (Linux/Android) 솔루션을 생각해 냈습니다.

#include <jni.h> 
#include <cassert> 
#include "JavaVM.h" 

namespace jni_interface{ 

    class ThisThread{ 
     inline static thread_local pthread_key_t p_key; 

     static void pthread_dstr(void *arg){ 
      if (!jni_env) return; 
      java_vm->DetachCurrentThread(); 
      jni_env = nullptr; 

      pthread_setspecific(p_key, NULL); 
      pthread_key_delete(p_key); 
     } 

     static void register_dstr(void *arg){ 
      { 
       const int res = pthread_key_create(&p_key, pthread_dstr); 
       assert(res != EAGAIN); 
       assert(res != ENOMEM); 
       assert(res == 0); 
      } 
      { 
       const int res = pthread_setspecific(p_key, arg); 
       assert(res == 0); 
      } 
     } 

     inline static thread_local JNIEnv* jni_env{nullptr}; 
    public: 
     JNIEnv* getEnv(){ 
      if (!jni_env){ 
       assert(java_vm); 
       java_vm->GetEnv((void**)&jni_env, JNI_VERSION); 
       java_vm->AttachCurrentThread(&jni_env, NULL);  // safe to call in main thread 

       register_dstr(jni_env); 
      } 

      return jni_env; 
     } 
    }; 

    static thread_local ThisThread this_thread; 
} 

심지어 어떤 이유에 의한 경우, pthread_dstr은 다음 호출에 C++의 정적 thread_locals (또는 인터리브) 마지막 사용하기 전에 파괴 즉 ThisThread으로는, (getEnv())에 이의를 제기하기 전에 호출됩니다 우리 좀 다시 초기화/그것을 다시 만들고 pthread_dstr을 다른 라운드에 등록하십시오.

N.B. 총 최대 값은 PTHREAD_DESTRUCTOR_ITERATIONS입니다. 이것은 4입니다.하지만 최악의 경우 (C++ thread_local 구현에서 p_thread 소멸자를 사용하면 [우리의 pthread_dstr이 마지막에 호출되지 않을 수도 있습니다. 첫 번째 라운드]).