多线程与临界区



windows下创建多线程:

 一个简单的例子

//winodws下多线程函数原型必需如下声明
//DWORD unsigned long
//WINAPI _stdcall
//函数名可以用任何合法名称替换
//LPVOID void *
DWORD WINAPI ThreadProc(LPVOID lpParamter);
 

int main()
{//多线程例子
    //win线程创建函数
    HANDLE hThrdPrc = CreateThread(NULL, //安全性 NULL为默认
        0, //初始栈的大小, 设为0或小于默认提交大小, 就会使用同调用函数的线程相同的栈空间大小.
        ThreadProc,//线程地址
        NULL, //线程接受的参数
        0, //附加标记, CREATE_SUSPENDED: 创建线程后要手动调用ResumeThread启动线程, 0为自动启动
        NULL/*一个结构体, 用来接收线程ID, 可为NULL*/);
    CloseHandle(hThrdPrc); //关闭句柄, 线程已启动, 表示对这个线程句柄没兴趣作别的事情

    std::cout<<"this is in main!\n";

    std::cin.get();
    return 0;
}

 
DWORD WINAPI ThreadProc(LPVOID lpParamter)
{
    std::cout<<"Thread in ThreadProc not in main\n";
    return 0;
}

这是一个带有返回值, 和传递多个参数的例子:

思路是把所有需要的参数和返回值封装到一个结构中:(先不考虑同步问题)

#include <windows.h> 
#include <iostream>
 

DWORD WINAPI ThreadProc(LPVOID lpParamter);
 

typedef struct
{//将多个参数和返回值放到结构中
    int a;
    int b;
    int sum;
    bool isOk; //判断线程是否完成
} Addtor, * PTRADD;

 
int main()
{
    Addtor adder;
    adder.a = 10;
    adder.b = 20;
    adder.isOk = false;

    HANDLE hThrdPrc = CreateThread(NULL, 
        0, 
        ThreadProc,
        (LPVOID)(&adder), //将结构体指针传递给线程
        0, 
        NULL);
    CloseHandle(hThrdPrc);

    while(!adder.isOk) {} //等待线程结果
    std::cout<<"Sum = "<<adder.sum<<std::endl;
    std::cout<<"done in main!\n";
 
    std::cin.get();
    return 0;
}

 
DWORD WINAPI ThreadProc(LPVOID lpParamter)
{
    PTRADD pa = (PTRADD)lpParamter;
    pa->sum = pa->a + pa->b;
    pa->isOk = true;

    return 0;
}


那么如果我们想在在类中建立主调线程来调用其他的线程呢?先看下面这个错误的例子:

#include <windows.h> 
#include <iostream>


class MyClass
{
public:
	unsigned long _stdcall MFun(void * lpParam)
	{
		std::cout<<"hello in MFun\n";
		return 0;
	}
	void Fun()
	{
		HANDLE hM = CreateThread(NULL, 0, this->MFun, NULL, 0, NULL);
	}
};

int main()
{
	

	std::cin.get();
	return 0;
}

这样是根本没有办法通过编译的, 大概的原因就是因为函数的调用约定不一至, 在C++类中的成员函数调用约定为_thiscall, 虽然在MFun函数已经显示声明了调用约定,但还是不能通过编译.

那么想要通过编译, 就要将MFun函数声明为静态的.或是声明为友元.

下面是友元方式:

#include <windows.h> 
#include <iostream>


class MyClass
{
public:
	friend unsigned long _stdcall MFun(void * lpParam);
	void Fun()
	{
		for (int i = 0; i < 10; i++)
		{
			HANDLE hM = CreateThread(NULL, 0, MFun, NULL, 0, NULL);
			CloseHandle(hM);
		}
	}
};

int main()
{
	MyClass m;
	m.Fun();

	std::cin.get();
	return 0;
}

unsigned long _stdcall MFun(void * lpParam)
{
	std::cout<<"hello in MFun\n";
	return 0;
}

说到多线程,就必需考虑线程同步, 这种方式主要是处理数据上对数据状态的要求, 比如看下面的例子:(没有同步)

#include <windows.h> 
#include <iostream>

unsigned long _stdcall MFun1(void * lpParam);
unsigned long _stdcall MFun2(void * lpParam);

int Afxcounts = 100; //全局变量

int main()
{
	HANDLE h1 = CreateThread(NULL, 0, MFun1, NULL, 0, NULL);
	HANDLE h2 = CreateThread(NULL, 0, MFun2, NULL, 0, NULL);
	CloseHandle(h1);
	CloseHandle(h2);
	std::cin.get();
	return 0;
}

unsigned long _stdcall MFun1(void * lpParam)
{
	while (Afxcounts >0)
	{
		std::cout<<"Thread in 1 : Afxcounts left : "<<--Afxcounts<<std::endl;
	}
	return 0;
}

unsigned long _stdcall MFun2(void * lpParam)
{
	while (Afxcounts >0)
	{
		std::cout<<"Thread in 2 : Afxcounts left : "<<--Afxcounts<<std::endl;
	}
	return 0;
}

这样在某些情况下是可行的, 但如果要求访问当时正确的Afxcounts时, 这个程序就无法办到了!

我们可以为这段代码加锁, 可以使用临界区的方式:

1, 在何适的地方声明一个临界区

2, 在何适的地方初始化临界区

3, 进入临界区.

4, 离开临界区, 原则是在哪进入, 就要在哪里离开.

5, 释放临界区, 原则是在哪初始化最好在哪释放.

请看下面的线程安全的例子,

#include <windows.h> 
#include <iostream>

unsigned long _stdcall MFun1(void * lpParam);
unsigned long _stdcall MFun2(void * lpParam);

int Afxcounts = 1000; //全局变量

CRITICAL_SECTION locker; //声明临界区.

int main()
{
	InitializeCriticalSection(&locker); //初始化临界区
	HANDLE h1 = CreateThread(NULL, 0, MFun1, NULL, 0, NULL);
	HANDLE h2 = CreateThread(NULL, 0, MFun2, NULL, 0, NULL);
	CloseHandle(h1);
	CloseHandle(h2);
	std::cin.get();
	DeleteCriticalSection(&locker); //释放临界区
	return 0;
}

unsigned long _stdcall MFun1(void * lpParam)
{
	while (Afxcounts >0)
	{
		EnterCriticalSection(&locker); //进入一定要注意位置, 如果是在while前面进入的话, 可以考虑会出现的情况
		std::cout<<"Thread in 1 : Afxcounts left : "<<--Afxcounts<<std::endl;
		LeaveCriticalSection(&locker); //离开临界区
	}
	return 0;
}

unsigned long _stdcall MFun2(void * lpParam)
{
	while (Afxcounts >0)
	{
		EnterCriticalSection(&locker);
		std::cout<<"Thread in 2 : Afxcounts left : "<<--Afxcounts<<std::endl;
		LeaveCriticalSection(&locker);

	}
	return 0;
}

对于更多于同步相关的信息, 这里将不再更多的讲解, 可以看之后说明同步问题的文章


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值