您的位置:首页 > 编程语言

windows 核心编程之8 用户模式下的线程同步

2013-08-15 12:13 309 查看
用户模式下的线程同步

有下面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);

也必须要成对使用

可以使用 TryEnterCriticalSection 来替换 EnterCriticalSection  这个函数会尝试请求资源,如果没有资源,返回false,有资源返回true,同时会更新关键段结构体,所以访问了资源后,可以调用 LeaveCriticalSection。 下面的代码说明情况了。

if(TryEnterCriticalSection(&cs))
{
//需要保护的资源

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没有效果,这个函数是没有使用上面那个函数,初始化关键段,在后期使用中,可以使用下面这个函数,来改变!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐