线程操作
回过头来再看一下这个东西,还是有那么几点是需要注意的。
线程优先级
就是线程的一个操作级别,分为很多种,高级别的先运行低级别的后运行(其实有32个,但是Windows提供的接口就那么几个)。
SetThreadPriority
通过这个函数就可以设置。
看下面的操作实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| #include <stdio.h> #include <windows.h>
DWORD WINAPI ThreadIdle(LPVOID l) { int i=0; while(i++<10) printf("Idel\n"); return 0; } DWORD WINAPI ThreadNormal(LPVOID l) { int i=0; while(i++<10) printf("Normal\n"); return 0; }
int main(int argc, TCHAR* argv[]) { DWORD dwThreadID; HANDLE h[2]; h[0] = CreateThread(NULL, 0, ThreadIdle, NULL, CREATE_SUSPENDED, &dwThreadID); SetThreadPriority(h[0],THREAD_PRIORITY_IDLE); ResumeThread(h[0]); h[1] = CreateThread(NULL, 0, ThreadNormal, NULL, 0, &dwThreadID); WaitForMultipleObjects(2,h,TRUE,INFINITE); CloseHandle(h[0]); CloseHandle(h[1]); return 0; }
|
比较简单,就是开两个线程,来看运行的先后顺序,先暂停第一个低级别,设置完级别之后就开第二个中级别的,最后等内核对象执行完释放。
但是可能我的CPU比较强大(嘻嘻嘻,12个肯定快喽),所以我这里基本就是间接运行的,在低版本XP的时候还是可以看出效果的,我这里的效果是:
可以看到Normal虽然在后面,但是还是有重叠,但是不稳定。
临界区对象
这个之前说过,而且操作起来相对简单,这里就不提了。
但有一点需要注意的是,这个临界区不是内核对象,所以不能再进程之间进行同步。
互锁函数
这个操作也是比较简单,但是就是单一了,只能对变量进行加减:
减:
1 2 3
| LONG InterlockedDecrement( LPLONG lpAddend );
|
加:
1 2 3
| LONG InterlockedIncrement( LPLONG lpAddend );
|
提供参数给他就能保证安全了。
内核事件对象
这个算是比较常用的,主要是有两点比较混淆。
分为自动重置和人工重置,区分就是:
- 自动重置的话呢,当我们的函数执行完毕没必要调用
ResetEvent
这个函数告诉其他线程,并且当我们执行完毕之后只有一个等待的对象会进行工作。
- 人工重置的话呢就是,执行完毕我们还要调用
ResetEvent
来让其他线程知道,但是这样会让所有线程都活过来。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <stdio.h> #include <windows.h> #include <process.h> HANDLE g_hEvent; UINT __stdcall ChildFunc(LPVOID); int main(int argc, TCHAR* argv[]) { HANDLE hChildThread; UINT uId; g_hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); hChildThread = (HANDLE)_beginthreadex(NULL,0,ChildFunc,NULL,0,&uId); printf("Please sure to working:"); getchar(); SetEvent(g_hEvent); WaitForSingleObject(hChildThread,INFINITE); printf("end"); CloseHandle(hChildThread); CloseHandle(g_hEvent); return 0; }
UINT __stdcall ChildFunc(LPVOID) { WaitForSingleObject(g_hEvent,INFINITE); printf("Working\n"); Sleep(5000); return 1; }
|
TLS局部存储
这个东西是非常的好用滴,就是说每个线程都有属于自己的一块线程位数组,每个位都是存放一个索引的,通过一个位可以存取和放置一个LPVOID的值,这个位是由主线程提供的。
简单地说就是主线程提供一个索引,子线程通过这个索引可以存取和获取这个索引对应的值,并且这个是独立,一个线程是没办法设置别的线程对应的值的。
看下例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
|
#include "stdafx.h"
#include <stdio.h> #include <windows.h> #include <process.h>
DWORD g_tlsUsedTime; void InitStartTime(); DWORD GetUsedTime();
UINT __stdcall ChildFunc(LPVOID); int _tmain(int argc, TCHAR* argv[]) { UINT uId; int i; HANDLE h[10]; g_tlsUsedTime = TlsAlloc();
for(i = 0;i<10;i++) h[i] = (HANDLE)_beginthreadex(NULL,0,ChildFunc,NULL,0,&uId); WaitForMultipleObjects(10,h,TRUE,INFINITE);
for(i = 0;i<10;i++) CloseHandle(h[i]);
TlsFree(g_tlsUsedTime);
system("pause"); return 0; }
UINT __stdcall ChildFunc(LPVOID) { int i; InitStartTime(); i = 10000*10000; while(i--){}; printf("ProcessID:%d,Used Time:%d\n",GetCurrentThreadId(),GetUsedTime()); return 0; }
void InitStartTime() { DWORD dwStart = GetTickCount(); TlsSetValue(g_tlsUsedTime,(LPVOID)dwStart); } DWORD GetUsedTime() { DWORD dwEnd = GetTickCount(); dwEnd = dwEnd-(DWORD)TlsGetValue(g_tlsUsedTime); return dwEnd; }
|
这个就是获取时间的一个验证程序。
思路比较简单,就是通过设置这个独特的位置开始的时间,然后运行完毕之后,再通过获取这个位来达到计时的效果。
简单的总结这个线程操作,就是公共的变量我们必须要保护起来,因为你不知道什么时候他就会出现错误,可能变成别的值,因为CPU是靠时间片来运行的,突然分给别的线程是很有可能的。