2017-11-09 3 views
1

Schwarz counter (aka Nifty counter)イディオムをthread_localと使用できますか?C++ Schwarzカウンタwith thread_local

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; 

が最初に構築されるためには、各スレッドの最後の破壊:

を(私はthread_localですべてstaticを置き換えると仮定)私は、この(JavaのJNIスレッドのヘルパー)が必要です。 他のstaticまたはthread_localオブジェクトのデストラクタ/コンストラクタからthis_thread->getEnv()を呼び出すことがあります。

UPDATE

https://stackoverflow.com/a/30200992 - ここでは、標準ではthread_localデストラクタは、静的前に呼び出さことを言うと、私は後になるこのいずれかが必要。

+0

thisThreadへの参照もthread_localにする必要があります。 –

+0

@RichardHodgesどういう意味ですか? – tower120

+0

秒、デモをノックします –

答えて

1

普通のシュワルツカウンタを実装するのが最善の解決策だと思うが、thread_localスタティックImplの点でThisThreadクラスを実装してください。

出力との完全な例:

// 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)ソリューションを考え出しました。

何らかの理由により、 pthread_dstrオブジェクトに次の呼び出しで、[最後に使用する前に破壊され言い換える ThisThreadに] C++の静的thread_locals(またはインターリーブされた)の前に呼び出されます場合でも
#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; 
} 

getEnv())我々ちょっと再初期化/それを再作成してpthread_dstrを別のラウンドに登録してください。

N.B.最大で我々はPTHREAD_DESTRUCTOR_ITERATIONSのラウンドを4つ持つことができますが、最悪の場合には2番目のスレッドに終わることになります(C++のthread_local実装でp_threadデストラクタを使用すると[OUR pthread_dstrが最後に呼び出されないことがあります第1ラウンド])。