在看书时,了解到boost线程中的yield方法:可以将本线程的CPU时间片放弃,并允许其他线程运行。认为其是一个操作线程之利器,所以写了个3个线程,循环打印ABC字符串,以验证其交出时间片功能。
代码如下:
#include <windows.h> #include <iostream> #include <boost/thread.hpp> #include <boost/atomic.hpp> #include <boost/ref.hpp> using namespace std; using namespace boost; enum MARK { A, B, C, }; mutex io_mutex; typedef boost::atomic<MARK> ENUM_MARK; void print_abc(ENUM_MARK& mark, MARK CurID) { for (int nIndex = 0; nIndex < 10;) { mutex::scoped_lock lock(io_mutex); switch (CurID) { case A: { if (mark == MARK::C) { cout << "A"; mark = MARK::A; nIndex++; } break; } case B: { if (mark == MARK::A) { cout << "B"; mark = MARK::B; nIndex++; } break; } case C: { if (mark == MARK::B) { cout << "C" << endl; mark = MARK::C; nIndex++; } break; } default: break; } // 加个yield,交出本线程时间片,让其他线程运行。 this_thread::yield(); } } int main() { ENUM_MARK mark = MARK::C; int nRetry = 0; // 创建3个线程,依次输出ABC。 // 连续循环3此,观察运行时间。 while (nRetry < 3) { DWORD nStart = ::GetTickCount(); thread t1(print_abc, boost::ref(mark), MARK::A); thread t2(print_abc, boost::ref(mark), MARK::B); thread t3(print_abc, boost::ref(mark), MARK::C); // 等待t3线程结束,因为其输出最后一个C。 t3.join(); DWORD nEnd = ::GetTickCount(); DWORD nTotal = nEnd - nStart; cout << "Total times:" << nTotal << endl; nRetry++; } getchar(); return 0; }
当我把this_thread::yield();注释掉后,再次运行,发现线程运行速度加快,平均10ms不到。
比较奇怪,查看yield的实现代码,才发现其实它就执行了Sleep(0),Sleep(0)的确会放弃CPU时间片,允许其他线程运行。但其它线程,也包含了放弃CPU时间片的线程,这样就可能造成单个线程无限次的放弃CPU时间片,又再一次获得运行权限。
this_thread::yield()的代码定义如下:
void yield() BOOST_NOEXCEPT { detail::win32::Sleep(0); }
#include <windows.h> #include <iostream> #include <string> using namespace std; CRITICAL_SECTION CK; enum MARK { A, B, C, }; struct MyStruct { volatile MARK* mark; MARK CurID; }; DWORD WINAPI Win32_Thread(LPVOID pStruct) { MyStruct* myStruct = (MyStruct*)(pStruct); for (int nIndex = 0; nIndex < 10;) { EnterCriticalSection(&CK); switch (myStruct->CurID) { case A: { if (*myStruct->mark == MARK::C) { cout << "A"; *myStruct->mark = MARK::A; nIndex++; } break; } case B: { if (*myStruct->mark == MARK::A) { cout << "B"; *myStruct->mark = MARK::B; nIndex++; } break; } case C: { if (*myStruct->mark == MARK::B) { cout << "C" << endl; *myStruct->mark = MARK::C; nIndex++; } break; } default: break; } LeaveCriticalSection(&CK); } return 0; } int main() { ::InitializeCriticalSection(&CK); MyStruct myStruct; myStruct.CurID = A; myStruct.mark = new MARK; *myStruct.mark = C; DWORD dwID1, dwID2, dwID3; HANDLE hThreadA = ::CreateThread(NULL, 0, &Win32_Thread, &myStruct, 0, &dwID1); MyStruct myStruct2; myStruct2.CurID = B; myStruct2.mark = myStruct.mark; HANDLE hThreadB = ::CreateThread(NULL, 0, &Win32_Thread, &myStruct2, 0, &dwID2); MyStruct myStruct3; myStruct3.CurID = C; myStruct3.mark = myStruct.mark; HANDLE hThreadC = ::CreateThread(NULL, 0, &Win32_Thread, &myStruct3, 0, &dwID3); getchar(); DeleteCriticalSection(&CK); return 0; }
以下是出现恶性竞争的代码:
#include <windows.h> #include <iostream> #include <string> using namespace std; CRITICAL_SECTION CK; enum MARK { A, B, C, }; struct MyStruct { volatile MARK* mark; MARK CurID; }; DWORD WINAPI Win32_Thread(LPVOID pStruct) { MyStruct* myStruct = (MyStruct*)(pStruct); for (int nIndex = 0; nIndex < 10;) { EnterCriticalSection(&CK); switch (myStruct->CurID) { case A: { if (*myStruct->mark == MARK::C) { cout << "A"; *myStruct->mark = MARK::A; nIndex++; } break; } case B: { if (*myStruct->mark == MARK::A) { cout << "B"; *myStruct->mark = MARK::B; nIndex++; } break; } case C: { if (*myStruct->mark == MARK::B) { cout << "C" << endl; *myStruct->mark = MARK::C; nIndex++; } break; } default: break; } // 这里加了Sleep,引起恶性竞争。 ::Sleep(1); LeaveCriticalSection(&CK); } return 0; } int main() { ::InitializeCriticalSection(&CK); MyStruct myStruct; myStruct.CurID = A; myStruct.mark = new MARK; *myStruct.mark = C; DWORD dwID1, dwID2, dwID3; HANDLE hThreadA = ::CreateThread(NULL, 0, &Win32_Thread, &myStruct, 0, &dwID1); MyStruct myStruct2; myStruct2.CurID = B; myStruct2.mark = myStruct.mark; HANDLE hThreadB = ::CreateThread(NULL, 0, &Win32_Thread, &myStruct2, 0, &dwID2); MyStruct myStruct3; myStruct3.CurID = C; myStruct3.mark = myStruct.mark; HANDLE hThreadC = ::CreateThread(NULL, 0, &Win32_Thread, &myStruct3, 0, &dwID3); getchar(); DeleteCriticalSection(&CK); return 0; }
1. yield方法其实就是::Sleep(0)。
2. Sleep会交出CPU时间片,允许其他线程运行,但“其他线程”也包含了交出CPU时间片的那个线程。
3. 想要更好的进行线程切换,不能够使用Sleep,而应采用线程锁或其他线程切换方法。
Boost编程之--慎用线程的this_thread::yield()方法
原文:http://blog.csdn.net/renstarone/article/details/19922091