用户模式下的线程同步
有下面2种情况,需要用到线程同步
1 需要让多个线程同时访问一个资源,同时不能破坏资源的完整性
2 一个线程需要通知另外一个线程,任务完成。
1 原子访问方式:
提供下面以下的原子方式访问的函数:
#include <Windows.h>
#include <stdio.h>
int main()
{
// 原子方式操作
LONG volatile ivVariable = 10;
LONGLONG volatile llVariable = 10;
// return initial value of first parameter
// you can use it to do subtraction, first parameter negative
LONG ltestVar = InterlockedExchangeAdd(&ivVariable, 1);
LONGLONG llTestVariable = InterlockedExchangeAdd64(&llVariable, 2);
//
BOOL volatile bTest = FALSE;
BOOL bOldValue = (BOOL)InterlockedExchange((LONG volatile*)&bTest, TRUE);
volatile int*p = new int(10);
LONG* lptest = new LONG(100);
LONG* plvalue = (LONG*)InterlockedExchangePointer(&p,lptest);
delete p;
delete lptest;
LONG volatile lvolatileCompateValue = 10;
// function 参数1与参数3是否相等,不相等不执行操作,
//相等时,把参数2赋值给参数1,返回值参数1以前的值
LONG lOldValue = InterlockedCompareExchange(&lvolatileCompateValue, 15, 11);
// 参数1 是一个指针
LONG volatile* lpvolatile = new LONG(11);
LONG* lp = new LONG(11);
LONG* lOldCompateValue =(LONG*) InterlockedCompareExchangePointer((volatile PVOID*)&lpvolatile,(PVOID)lp,(PVOID)lpvolatile);
return 0;
}
下面是原子访问的链表栈
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
// 原子方式访问单向链表
typedef struct mydata
{
int i;
TCHAR ch[10];
double dou;
}LISTDATA;
typedef struct _Slist_item
{
SLIST_ENTRY m_entry;
LISTDATA m_data;
}LISTTIME,*PLISTTIME;
int main()
{
SLIST_HEADER head;
SLIST_ENTRY slistEntry;
LISTTIME ltem;
PLISTTIME pLitem = NULL;
InitializeSListHead(&head);
for (int i = 0; i< 10; i++)
{
pLitem = new LISTTIME;
pLitem->m_data.i = i;
pLitem->m_data.dou = 100;
_tcscpy_s(pLitem->m_data.ch,10,TEXT("asdf"));
InterlockedPushEntrySList( &head, &pLitem->m_entry );
}
LONG lSize = QueryDepthSList(&head);
for (int i = 0; i < 10; ++i)
{
pLitem = (PLISTTIME)InterlockedPopEntrySList(&head);
delete pLitem;
}
InterlockedFlushSList(&head);
return 0;
}
2 高速缓存行
Windows SDK 代码最后释放内存有错,buffer指针移动了,下面的代码,已经改好,增加了一些新信息
#include <windows.h>
#include <malloc.h>
#include <stdio.h>
#include <tchar.h>
typedef BOOL (WINAPI *LPFN_GLPI)(
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION,
PDWORD);
int _cdecl _tmain ()
{
BOOL done;
BOOL rc;
DWORD returnLength;
DWORD procCoreCount;
DWORD byteOffset;
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer, FreeBuffer;
LPFN_GLPI Glpi;
Glpi = (LPFN_GLPI) GetProcAddress(
GetModuleHandle(TEXT("kernel32")),
"GetLogicalProcessorInformation");
if (NULL == Glpi)
{
_tprintf(
TEXT("GetLogicalProcessorInformation is not supported.\n"));
return (1);
}
done = FALSE;
buffer = NULL;
returnLength = 0;
while (!done)
{
rc = Glpi(buffer, &returnLength);
if (FALSE == rc)
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
if (buffer)
free(buffer);
FreeBuffer = buffer=(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(
returnLength);
if (NULL == buffer)
{
_tprintf(TEXT("Allocation failure\n"));
return (2);
}
}
else
{
_tprintf(TEXT("Error %d\n"), GetLastError());
return (3);
}
}
else done = TRUE;
}
procCoreCount = 0;
byteOffset = 0;
while (byteOffset < returnLength)
{
switch (buffer->Relationship)
{
case RelationProcessorCore:
procCoreCount++;
break;
case RelationNumaNode:
printf("节点信息:\n");
printf("numaNode = %d\n",buffer->NumaNode.NodeNumber);
break;
case RelationCache:
printf("CPU 高速缓冲区\n");
printf("关联性 %d\n",buffer->Cache.Associativity);
printf("Cache %d 缓存\n",buffer->Cache.Level);
printf("Cache %d 缓存行\n",buffer->Cache.LineSize );
printf("Cache %dKB 大小\n", buffer->Cache.Size / 1024);
switch(buffer->Cache.Type)
{
case CacheData:
printf("CacheData\n");
break;
case CacheInstruction:
printf("CacheInstruction\n");
break;
case CacheTrace:
printf("CacheTrace\n");
break;
case CacheUnified:
printf("CacheUnified\n");
break;
}
break;
case RelationProcessorPackage:
printf("RelationProcessorPackage:\n");
break;
default:
break;
}
byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
buffer++;
printf("=========================================================\n");
}
_tprintf(TEXT("Number of active processor cores: %d\n"),
procCoreCount);
free (FreeBuffer);
return 0;
}
3 高级线程同步
不要使用旋转锁的方式来,线程之间同步
4 关键段
CRITICAL_SECTION 是关键段的结构体,我们不需要了解结构体的细节,帮助文档也没有它的说明.
下面是初始化关键段得函数,参数是结构体地址
InitializeCriticalSection(&cs);
DeleteCriticalSection(&cs); 这个函数是删除,关键段结构体,必须成对使用,否则资源泄露
EnterCriticalSection(&cs);
//需要保护的资源
LeaveCriticalSection(&cs);
//需要保护的资源
LeaveCriticalSection(&cs);
也必须要成对使用
可以使用 TryEnterCriticalSection 来替换 EnterCriticalSection 这个函数会尝试请求资源,如果没有资源,返回false,有资源返回true,同时会更新关键段结构体,所以访问了资源后,可以调用 LeaveCriticalSection。 下面的代码说明情况了。
if(TryEnterCriticalSection(&cs))
{
//需要保护的资源
LeaveCriticalSection(&cs);
}
else
{
//没有资源可以访问
//不需要调用 LeaveCriticalSection;
}
{
//需要保护的资源
LeaveCriticalSection(&cs);
}
else
{
//没有资源可以访问
//不需要调用 LeaveCriticalSection;
}
下面的代码介绍了怎么使用关键段
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#include <process.h>
class MyCriticalSection
{
public:
MyCriticalSection(){
InitializeCriticalSection(&m_cs);
}
~MyCriticalSection(){
DeleteCriticalSection(&m_cs);
}
void Lock()
{
EnterCriticalSection(&m_cs);
}
void UnLock()
{
LeaveCriticalSection(&m_cs);
}
BOOL TryLock()
{
return TryEnterCriticalSection(&m_cs);
}
void TryUnLock()
{
UnLock();
}
private:
CRITICAL_SECTION m_cs;
};
int gSum = 0;
MyCriticalSection criticalsection;
unsigned int WINAPI ThreadFuncOne(LPVOID lparam)
{
criticalsection.Lock();
for (int i = 0; i < 100; ++i)
{
gSum++;
}
criticalsection.UnLock();
return 0;
}
unsigned int WINAPI ThreadFuncTwo(LPVOID lparam)
{
criticalsection.Lock();
for (int i = 0; i < 100; ++i)
{
gSum++;
Sleep(10);
}
criticalsection.UnLock();
return 0;
}
unsigned int WINAPI ThreadFuncThree(LPVOID lparam)
{
while(!criticalsection.TryLock())
{
printf("没有资源可以使用\n");
Sleep(10);
}
printf("资源可以使用\n");
for (int i = 0; i < 100; ++i)
{
gSum++;
}
criticalsection.TryUnLock();
return 0;
}
int main(int argc, char **argv)
{
HANDLE hThread1 = (HANDLE)_beginthreadex(NULL, 0, ThreadFuncOne, NULL, 0, NULL);
HANDLE hThread2 = (HANDLE)_beginthreadex(NULL, 0, ThreadFuncTwo, NULL, 0, NULL);
HANDLE hThread3 = (HANDLE)_beginthreadex(NULL, 0, ThreadFuncThree, NULL, 0, NULL);
HANDLE harray[3] = {hThread1,hThread2,hThread3};
WaitForMultipleObjects(_countof(harray),harray,TRUE,INFINITE);
printf("sum = %d\n", gSum);
system("pause");
return 0;
}
初始化的时候可以使用
//初始化的时候,使用旋转锁 单CPU上没有作用
WINBASEAPI
BOOL
WINAPI
InitializeCriticalSectionAndSpinCount(
__out LPCRITICAL_SECTION lpCriticalSection,
__in DWORD dwSpinCount
);
SetCriticalSectionSpinCount 设置旋转锁的次数,单CPU没有效果,这个函数是没有使用上面那个函数,初始化关键段,在后期使用中,可以使用下面这个函数,来改变!