std::thread etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
std::thread etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

5 Aralık 2019 Perşembe

std::thread Constructor Metodu ve Arg Parametresi

constructor - f parametresi + Arg parametresi
f parametresi yani functor için açıklama şöyle.
all state in the thread before the new thread is spawned is visible to the spawned thread.
f parametresinin overload halini seçmek
Örnek
Elimizde overload edilmiş iki metod olsun.
int do_calculation(int x)    
{    
  std::cout<<x;    
}    

float do_calculation(float x)    
{    
  std::cout<<x;    
}    
Overload için şöyle yaparız.
std::thread t1(static_cast<int(*)(int)>(do_calculation),20);    
std::thread t2(static_cast<float(*)(float)>(do_calculation),200.0f);  
Arg Parametresi İçin Kurallar

Açıklaması şöyle.
The arguments to the thread function are moved or copied by value. If a reference argument needs to be passed to the thread function, it has to be wrapped (e.g. with std::ref or std::cref).
Bu constructor'ın imzası variadic arguments alacak şekilde tanımlı. Dolayısıyla içeride muhtemelen şöyle kullanılıyor.
template <typename Function, typename... Arg>
void call_with_any(Function f, Arg&&... args) {
    f(std::forward<Arg>(args)...);
}
Ya da şöyle.
template <typename Function, typename... Arg>
void call_with_any(Function&& f, Arg&&... args) {
    std::forward<Function>(f)(std::forward<Arg>(args)...);
}
Argument'ın copy constructor metodu yoksa
Açıklaması şöyle.
The constructor of std::thread first creates a copy of its first argument (by decay_copy) — that is where copy constructor is called. (Note that in case of an rvalue argument, such as thread t1{std::move(t)}; or thread t1{tFunc{}};, move constructor would be called instead.)

The result of decay_copy is a temporary that resides on the stack. However, since decay_copy is performed by a calling thread, this temporary resides on its stack and is destroyed at the end of std::thread::thread constructor. Consequently, the temporary itself cannot be used by a new created thread directly.
Örnek
Elimizde şöyle bir kod olsun.
class tFunc{
  
public:
  tFunc(){
    cout<<"Constructed : "<<this<<endl;
  }
  ~tFunc(){
    cout<<"Destroyed : "<<this<<endl;
  }
  void operator()(){
    cout<<"Thread running at : "<<x<<endl;
  }
};

int main()
{
  tFunc t;
  thread t1(t);
  if(t1.joinable())
  {
    cout<<"Thread is joining..."<<endl;
    t1.join();
  }
  return 0;
}
Çıktı olarak şunu alırız. Copy constructor olmadığı için temporary nesnenin yaratıldığın görmeyiz ancak yok olduğunu görürüz.
Constructed : 0x7ffe27d1b0a4 //main thread
Destroyed : 0x7ffe27d1b06c //Temporary
Thread is joining...
Thread running at : 11
Destroyed : 0x2029c28  //thread
x : 1
Destroyed : 0x7ffe27d1b0a4 //main thread
Argument'ın copy constructor metodu varsa
Örnek
Çıktı olarak şunu alırız.
Constructed : 0x104055020 //main thread
Copy constructed : 0x104055160 (source=0x104055020) //temporary on main thread
Copy constructed : 0x602000008a38 (source=0x104055160) //copy on thread
Destroyed : 0x104055160 //temporary on main thread destroyed
Thread running at : 11
Destroyed : 0x602000008a38 //thread object is destroyed
Thread is joining...
x : 1
Destroyed : 0x104055020 //main thread object is destroyed
Argument'ın copy ve move constructor metodları varsa
Örnek
Çıktı olarak şunu alırız.
Constructed : 0x104057020 /main thread
Copy constructed : 0x104057160 (source=0x104057020) //temporary on main thread
Move constructed : 0x602000008a38 (source=0x104057160) //move on thread
Destroyed : 0x104057160 //temporay on main thread destroyed
Thread running at : 11
Destroyed : 0x602000008a38 //thread object is destroyed
Thread is joining...
x : 1
Destroyed : 0x104057020 //main thread object is destroyed
Argument'ı std::ref ile geçersek
Örnek
Çıktı olarak şunu alırız.
Constructed : 0x104057020 //main therad
Thread is joining...
Thread running at : 11
x : 11
Destroyed : 0x104057020 //main thread object is destroyed
Örnek
Foo tipinden olan a nesnesinin bar metodunu işletmek ve m parametresini ref. olarak geçmek için C++17 ile şöyle yaparız.
class foo
{
public:
  void bar( std::mutex &m )
  {
    std::lock_guard<std::mutex> lock( m );
    std::cout << "Threading this function works!" << std::endl;
  }
};


int main()
{
  foo a;
  std::mutex m;
  std::thread bar_thread( &foo::bar, std::ref( a ), std::ref( m ) );
  bar_thread.join();
  return 0;
}
Örnek
std::ref çalışmıyorsa C++11 ile şöyle yaparız.
std::thread bar_thread( &foo::bar, std::addressof( a ), std::ref( m ) );
Argument'ı rvalue kullanırsak
Örnek
Çıktı olarak şunu alırız.
Constructed : 0x104055040 //rvalue
Move constructed : 0x104055160 (source=0x104055040) //temporary on main thread
Move constructed : 0x602000008a38 (source=0x104055160) //move on thread
Destroyed : 0x104055160 //temporary on main thread destroyed
Destroyed : 0x104055040 //rvalue is destroyed
Thread is joining...
Thread running at : 11
Destroyed : 0x602000008a38 //thread object is destroyed

17 Temmuz 2019 Çarşamba

std::thread Sınıfı

std::thread sınıfı
constructor - default
Thread'in default constructor'ı kullanılırsa nesne yaratılsa bilse thread başlamaz. İleriki bir noktada move assignment operator'ü ile thread başlatılıp, bizim nesnemize atanabilir. Şöyle yaparız.
m_Thread = std::thread(&Foo::loop, this);
constructor - method
std::thread constructor metodu yazısına bakmak önemli.

move constructor
std::thread move edilince içindeki functor std::invoke ile çalıştığı için sorun çıkmıyor. Açıklaması şöyle
A std::thread does not formally own the functor it was constructed with. Rather, the functor is held by the invocation of std::invoke which is kicked off (in the target thread) by the constructor.

So moving a std::thread doesn't need to, and isn't going to, move the functor. As long as the thread is executing, the functor is alive as a local variable.
destructor 
Joinable thread nesnesi thread bitmeden destruct edilirse uygulama sonlanır. Bu yüzden std::shared_ptr<std::thread> pek mantıklı değil.
30.3.1.3 thread destructor [thread.thread.destr] ~thread();

If joinable(), calls std::terminate(). Otherwise, has no effects.

[ Note: Either implicitly detaching or joining a joinable() thread in its destructor could result in difficult to debug correctness (for detach) or performance (for join) bugs encountered only when an exception is raised. Thus the pro grammer must ensure that the destructor is never executed while the thread is still joinable. — end note ]

Ya thread'in bitmesini beklemek ya da aşağıdaki gibi detach olmak gerekir.

detach metodu
Eğer thread scope içinde yaratılıyorsa ve sonucunu beklemek istemiyorsak detach olmamız gerekir.
std::thread thread(ClientLoop,clientSocket);
thread.detach();
// OK to destroy now
Aslında tedbirli olmak için şöyle kodlamak daha iyi.
if (thread.joinable())
  thread.detach ();
detach olurken şuna dikkat etmek gerekir. Eğer thread metodu exception atarsa ve bunu yakalamıyorsa std::terminate çağrılır. Yani şöyle bir kod tüm uygulamayı sonlandırır.
void aFunction() {
   std::thread t {someOtherFunction}; // someOtherFunction may throw!!!
   t.detach;
}
get_id metodu
std::thread::id tipindendir. İşletim sistemi tarafından thread'e atanan tekil numarayı döner. Windows'ta altta GetCurrentThreadId () metodunu çağırıyor. std::this_thread::get_id() ile aynı işlevi yerine getirir.
const std::thread::id MAIN_THREAD_ID = std::this_thread::get_id();
hardware_concurrency metodu
 - İşlemciler genellikle aynı hızlarda oluyorlar. Buna symmetric multiprocessing deniliyor.
 - hyperthreading ise şöyle.
The Intel hyper-threading technology is often disappointing. You probably don't want to have 8 active threads on an Intel processor with 4 cores and 8 hyperthreads.
Örnek
Şöyle yaparız.
//may return 0 when not able to detect
unsigned concurentThreadsSupported = std::thread::hardware_concurrency();
join metodu
Açıklaması şöyle.
With POSIX pthreads, child threads must be joined after they have exited, or else they continue to occupy system resources (like a process table entry in the kernel). This is done via pthread_join(). Windows has a somewhat analogous issue if the process holds a HANDLE to the child thread; although Windows doesn't require a full join, the parent must still call CloseHandle() to release its refcount on the process.

Since std::thread is a cross-platform abstraction, it's constrained by the POSIX requirement which requires the join.
joinable metodu
Thread'in çalışıp çalışmadığını anlamak için kullanılabilir. Çalışan thread - detach edilmediyse - true döner.
class Service
{
public:
    void start();
private:
    std::thread thread;
};
Service::start()
{
  if (thread.joinable())
    return;
}
Scott Meyers'in kitabında şöyle bir yardımcı sınıf var.
class ThreadRAII {
public:
  ThreadRAII(std::thread&& thread): t(std::move(thread)) {}
  ~ThreadRAII() { if (t.joinable()) (t.join(); }

private:
  std::thread t;
};
Bu sınıf r-value ile çalışıyor. reference ile çalışan bir sınıf şöyle.
class ThreadGuard {
public:
  ThreadGuard(std::thread &t_): t(t_) {}
  ~ThreadGuard()
  {
    if(t.joinable()) 
      t.join();
  }
private:
  std::thread &t;
};
Şöyle kullanırız.
void func()
{
    std::thread my_thread(f);
    ThreadGuard thread_guard(my_thread);
}
operator = metodu
Açıklaması şöyle. Bu davranış boos::thread sınıfından farklılık gösteriyor.
"If [the thread object] is joinable, terminate() is called."
Örnek
Şöyle yaparız.
std::thread td1;
td1 = std::thread(func);
Örnek
Elimizde şöyle bir kod olsun.
class ThreadTest 
{
private:
  std::thread mythread;

  int doSomeWork(){
    ...
  }

public:
  void runThread(){
    mythread = std::thread(&ThreadTest::doSomeWork, this);
  }
  ...
};
Eğer şöyle yaparsak yani çalışmakta olan bir thread'i tekrar thread ataması yaparsak
ThreadTest test;
test.runThread();
std::this_thread::sleep_for (std::chrono::seconds(10));
test.runThread();
Çıktı olarak şunu alırız.
terminate called without an active exception
sleep_for metodu
Açıklaması şöyle. Bu metod verilen değerden daha fazla süre uyuyabilir.
Blocks the execution of the current thread for at least the specified sleep_duration.
This function may block for longer than sleep_duration due to scheduling or resource contention delays.
Örnek
Açıklaması şöyle.
Instead of calling sleep_for, yielding the thread's execution slot, you could busy-sleep. That is, loop until a certain amount of time has passed. It'll often get more accurate results at the cost of making that CPU thread unusable for doing anything else.
Şöyle yaparız. Burada calc_overhead() metodunda 100ms geçmesini bulabilmek için harcanan süre hesaplanıyor ve bu süre ikiye bölünüyor.
// get a rough estimate of how much overhead there is in calling buzy_sleep()
std::chrono::nanoseconds calc_overhead() {
    using namespace std::chrono;

    auto init = []() {
        auto end = steady_clock::now() + 100ms;
        while(true) if(steady_clock::now() > end) break;
    };

    time_point<steady_clock> end;
    auto start=steady_clock::now();
    init();
    end = steady_clock::now();

    // The /2 is non-scientific but makes it sleep longer than you want more often than
    // shorter.
    return (end - start - 100ms) / 2;
}

// initialize the overhead constant that will be used in busy_sleep()
static const std::chrono::nanoseconds overhead = calc_overhead();

inline void busy_sleep(std::chrono::nanoseconds t) {
    auto end = std::chrono::steady_clock::now() + t - overhead;
    while(true) if(std::chrono::steady_clock::now() > end) break;
}
swap metodu
Şöyle yaparız.
std::thread td1;
...
std::thread(func).swap(td1);
thread ve exception
Exception thread metodundan kaçmamalıdır. Şöyle yapamayız.
void runComponent()
{
  throw std::runtime_error("oh no!\n");
}

auto control_thread = std::thread(runComponent);
boost sınıfına göre bazı eksiklikler
std::thread ile bazı şeyleri yapmak mümkün değil. Açıklaması şöyle. Bu işler için sınıfın native_handle() metodu kullanılabilir.
- No stack size on thread creation.
- No thread priorities.
Stack boyutunu ayarlamak için boost ile şöyle yapıyoruz.
boost::thread::attributes attrs;
attrs.set_size(4096*10);
boost::thread myThread(attrs, fooFunction, 42);
thread vector
Eğer thread'leri vector içinde kullanmak istersek
std::vector<std::thread> threads;
while (whatever){
    clientSocket = accept(listenSocket, nullptr, nullptr);
    threads.emplace_back(ClientLoop,clientSocket);
}

// later
for (std::thread & t : threads) {
    t.join();
}

// OK to destroy now
threads.clear();