C++ 多線程

簡介

thread 類表示單獨的執行線程。

一個已初始化的 thread 對象表示一條活躍的執行線程,且它是 joinable 的,有一個唯一的線程 id 。

一個默認構造的 thread 對象(未初始化)不是 joinable 的,且它的線程 id 和所有 non-joinable 的線程一樣。

一個 joinable 的線程通過移動、調用 join() 或 detach() 變爲 non-joinable 的。

創建線程

default (1) thread() noexcept;
initialization (2) template <class Fn, class... Args> explicit thread (Fn&& fn, Args&&... args);
copy [deleted] (3) thread (const thread&) = delete;
move (4) thread (thread&& x) noexcept;

fn:可調用對象,包括函數指針、指向成員函數的指針、函數對象(重載了 operator()、閉包)。

args:傳給 fn 的參數。

// constructing threads
#include <iostream>
#include <atomic>
#include <thread>
#include <vector>

std::atomic<int> global_counter(0);

void increase_global(int n) { for (int i = 0; i < n; ++i) ++global_counter; }

void increase_reference(std::atomic<int>& variable, int n) { for (int i = 0; i < n; ++i) ++variable; }

struct C : std::atomic<int> {
	C() : std::atomic<int>(0) {}
	void increase_member(int n) {
        for (int i = 0; i < n; ++i) fetch_add(1);
    }
};

int main() {
	std::vector<std::thread> threads;

	std::cout << "increase global counter with 10 threads...\n";
	for (int i = 1; i <= 10; ++i)
		threads.push_back(std::thread(increase_global, 1000));

	std::cout << "increase counter (foo) with 10 threads using reference...\n";
	std::atomic<int> foo(0);
	for (int i = 1; i <= 10; ++i)
		threads.push_back(std::thread(increase_reference, std::ref(foo), 1000));

	std::cout << "increase counter (bar) with 10 threads using member...\n";
	C bar;
	for (int i = 1; i <= 10; ++i)
		threads.push_back(std::thread(&C::increase_member, std::ref(bar), 1000));

	std::cout << "synchronizing all threads...\n";
	for (auto& th : threads) th.join();

	std::cout << "global_counter: " << global_counter << '\n';
	std::cout << "foo: " << foo << '\n';
	std::cout << "bar: " << bar << '\n';

	return 0;
}
increase global counter using 10 threads...
increase counter (foo) with 10 threads using reference...
increase counter (bar) with 10 threads using member...
synchronizing all threads...
global_counter: 10000
foo: 10000
bar: 10000

移動

move (1) thread& operator= (thread&& rhs) noexcept;
copy [deleted] (2) thread& operator= (const thread&) = delete;
// example for thread::operator=
#include <iostream>
#include <thread>
#include <chrono>
 
void pause_thread(int n)  {
  std::this_thread::sleep_for (std::chrono::seconds(n));
  std::cout << "pause of " << n << " seconds ended\n";
}

int main()  {
  std::thread threads[5];                    // default-constructed threads

  std::cout << "Spawning 5 threads...\n";
  for (int i=0; i<5; ++i)
    threads[i] = std::thread(pause_thread,i+1);   // move-assign threads

  std::cout << "Done spawning threads. Now waiting for them to join:\n";
  for (int i=0; i<5; ++i)
    threads[i].join();

  std::cout << "All threads joined!\n";

  return 0;
}
Spawning 5 threads...
Done spawning threads. Now waiting for them to join:
pause of 1 seconds ended
pause of 2 seconds ended
pause of 3 seconds ended
pause of 4 seconds ended
pause of 5 seconds ended
All threads joined!

線程 ID

id get_id() const noexcept;
// thread::get_id / this_thread::get_id
#include <iostream>
#include <thread>
#include <chrono>
 
std::thread::id main_thread_id = std::this_thread::get_id();

void is_main_thread() {
  if ( main_thread_id == std::this_thread::get_id() )
    std::cout << "This is the main thread.\n";
  else
    std::cout << "This is not the main thread.\n";
}

int main()  {
  is_main_thread();
  std::thread th (is_main_thread);
  th.join();
}
This is the main thread.
This is not the main thread.

join

bool joinable() const noexcept;

線程是否爲 joinable 。

void join();

等待,直到線程終止才返回。

// example for thread::joinable and join
#include <iostream>
#include <thread>
 
void mythread() {
  // do stuff...
}
 
int main() {
  std::thread foo;
  std::thread bar(mythread);

  std::cout << "Joinable after construction:\n" << std::boolalpha;
  std::cout << "foo: " << foo.joinable() << '\n';
  std::cout << "bar: " << bar.joinable() << '\n';

  if (foo.joinable()) foo.join();
  if (bar.joinable()) bar.join();

  std::cout << "Joinable after joining:\n" << std::boolalpha;
  std::cout << "foo: " << foo.joinable() << '\n';
  std::cout << "bar: " << bar.joinable() << '\n';

  return 0;
}
Joinable after construction:
foo: false
bar: true
Joinable after joining:
foo: false
bar: false

detach

void detach();

將 thread 對象表示的線程和調用線程分離,之後,它們彼此獨立,各自執行。當兩個之一執行結束時,被分離的線程會釋放其資源。

#include <iostream>
#include <thread>
#include <chrono>
 
void pause_thread(int n) {
  std::this_thread::sleep_for (std::chrono::seconds(n));
  std::cout << "pause of " << n << " seconds ended\n";
}
 
int main() {
  std::cout << "Spawning and detaching 3 threads...\n";
  std::thread (pause_thread,1).detach();
  std::thread (pause_thread,2).detach();
  std::thread (pause_thread,3).detach();
  std::cout << "Done spawning threads.\n";

  std::cout << "(the main thread will now pause for 5 seconds)\n";
  // give the detached threads time to finish (but not guaranteed!):
  pause_thread(5);
    
  return 0;
}
Spawning and detaching 3 threads...
Done spawning threads.
(the main thread will now pause for 5 seconds)
pause of 1 seconds ended
pause of 2 seconds ended
pause of 3 seconds ended
pause of 5 seconds ended

交換

成員函數 void swap (thread& x) noexcept;
非成員函數 void swap (thread& x, thread& y) noexcept;

交換兩個 thread 對象的狀態。

// swap
#include <iostream>
#include <thread>
#include <chrono>

void pause_thread(int n) {
	std::this_thread::sleep_for(std::chrono::seconds(n));
}

int main() {
	std::thread th1(pause_thread, 1);
	std::thread th2(pause_thread, 1);

	std::cout << "before swap: " << std::endl;
	std::cout << "thread1's id: " << th1.get_id() << std::endl;
	std::cout << "thread2's id: " << th2.get_id() << std::endl;

	th1.swap(th2);

	std::cout << "\nafter swap: " << std::endl;
	std::cout << "thread1's id: " << th1.get_id() << std::endl;
	std::cout << "thread2's id: " << th2.get_id() << std::endl;

	th1.join();
	th2.join();

	return 0;
}
before swap:
thread1's id: 4688
thread2's id: 5992

after swap:
thread1's id: 5992
thread2's id: 4688

支持的線程數

static unsigned hardware_concurrency() noexcept;

只是一個提示值,當該值不可計算時,它返回 0.

操作當前線程

std::this_thread 名稱空間中。

線程 ID

thread::id get_id() noexcept;

返回調用線程的線程 ID。

線程讓步

void yield() noexcept;

使調用線程讓步,即重新調度。

// this_thread::yield example
#include <iostream>
#include <thread>
#include <atomic>

std::atomic<bool> ready(false);

void count1m() {
	while (!ready) {             // wait until main() sets ready...
		std::this_thread::yield();
	}
	for (volatile int i = 0; i < 1000000; ++i) {}
}

int main() {
	std::thread th(count1m);

	ready = true;               // go!

	th.join();

	std::cout << '\n';

	return 0;
}

線程休眠

睡到某一時刻 template <class Clock, class Duration> void sleep_until (const chrono::time_point<Clock,Duration>& abs_time);
睡一段時間 template <class Rep, class Period> void sleep_for (const chrono::duration<Rep,Period>& rel_time);
// this_thread::sleep_until example
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
#include <ctime>

int main() {
  using std::chrono::system_clock;
    
  std::time_t tt = system_clock::to_time_t (system_clock::now());
  struct std::tm * ptm = std::localtime(&tt);
  std::cout << "Current time: " << std::put_time(ptm,"%X") << '\n';

  std::cout << "Waiting for the next minute to begin...\n";
  ++ptm->tm_min; ptm->tm_sec=0;
  std::this_thread::sleep_until (system_clock::from_time_t (mktime(ptm)));

  std::cout << std::put_time(ptm,"%X") << " reached!\n";

  return 0;
}
Current time: 11:52:36
Waiting for the next minute to begin...
11:53:00 reached!
// this_thread::sleep_for example
#include <iostream>
#include <thread>
#include <chrono>
 
int main() {
  std::cout << "countdown:\n";
  for (int i=3; i>0; --i) {
    std::cout << i << std::endl;
    std::this_thread::sleep_for (std::chrono::seconds(1));
  }
  std::cout << "Lift off!\n";

  return 0;
}
countdown:
3
2
1
Lift off!
發佈了118 篇原創文章 · 獲贊 18 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章