Boost编程之--慎用线程的this_thread::yield()方法
来源:互联网 发布:淘宝店招设计方法 编辑:程序博客网 时间:2024/06/16 08:16
在看书时,了解到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;}
运行后,3个线程打印10次ABC所耗的时间大约是30ms。发现30ms有点长,是不是yield引起的?
当我把this_thread::yield();注释掉后,再次运行,发现线程运行速度加快,平均10ms不到。
比较奇怪,查看yield的实现代码,才发现其实它就执行了Sleep(0),Sleep(0)的确会放弃CPU时间片,允许其他线程运行。但其它线程,也包含了放弃CPU时间片的线程,这样就可能造成单个线程无限次的放弃CPU时间片,又再一次获得运行权限。
this_thread::yield()的代码定义如下:
void yield() BOOST_NOEXCEPT { detail::win32::Sleep(0); }
为了更好的验证一下自己的推论,我直接使用了Win32 API的线程代码:
#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;}
像上面这样,没有用Sleep(0),3个线程都快速的输出了ABC。但如果我加了个Sleep(1)在线程的LeaveCriticalSection(&CK);前面,就发生了恶性竞争,基本上是每隔几秒钟,才输出ABC中的一个字母。
以下是出现恶性竞争的代码:
#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,而应采用线程锁或其他线程切换方法。
1 0
- Boost编程之--慎用线程的this_thread::yield()方法
- 线程的Yield方法
- 线程理解之yield方法
- boost::this_thread::sleep_for()死锁
- java基础:线程方法之yield方法
- std::this_thread::yield()使用理解
- 线程的yield方法和jion方法
- Java 线程的sleep和yield方法
- 线程的sleep,join,yield方法
- 线程的礼让(Thread.yield())方法
- Java线程的yield方法测试
- 线程的礼让(Thread.yield())方法
- 线程 Yield方法
- 线程(4)--yield方法
- 线程中的yield()方法
- 线程之sleep(),wait(),yield(),join()等等的方法的区别
- 自学java之线程方法(sleep、join、yield)
- 线程的sleep()方法和yield()方法的区别
- linux网络编程2-服务器设计
- 在基于对话框的程序中高级定制Web控件
- hdu-1325-Is It A Tree?--并查集判断是否是一棵树
- 数据结构之单链表
- CS/BS架构区别
- Boost编程之--慎用线程的this_thread::yield()方法
- redis 命令汇总
- MongoDB安装成为Windows服务及日常使用遇到问题总结
- OpenGL: 绘制按钮
- Java中-classpath和路径的使用
- java web开发:ajax技术(一)——局部刷新技术的实现
- 宏定义的妙用
- vim二进制文件打开
- saas特性