C++ 如何实现多线程与线程同步

CreateThread 实现多线程:

先来创建一个简单的多线程实例,无参数传递版,运行实例会发现,主线程与子线程运行无规律。

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

using namespace std;

DWORD WINAPI Func(LPVOID lpParamter)
{
	for (int x = 0; x < 10; x++)
	{
		cout << "thread function" << endl;
		Sleep(200);
	}
	return 0;
}

int main(int argc,char * argv[])
{
	HANDLE hThread = CreateThread(NULL, 0, Func, NULL, 0, NULL);
	CloseHandle(hThread);

	for (int x = 0; x < 10; x++)
	{
		cout << "main thread" << endl;
		Sleep(400);
	}

	system("pause");
	return 0;
}

beginthreadex 实现多线程:

这个方法与前面的CreateThread使用完全一致,只是在参数上面应使用void *该参数可以强转为任意类型,两者实现效果完全一致。

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

using namespace std;

unsigned WINAPI Func(void *arg)
{
	for (int x = 0; x < 10; x++)
	{
		cout << "thread function" << endl;
		Sleep(200);
	}
	return 0;
}

int main(int argc, char * argv[])
{
	HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, Func, NULL, 0, NULL);
	CloseHandle(hThread);
	for (int x = 0; x < 10; x++)
	{
		cout << "main thread" << endl;
		Sleep(400);
	}

	system("pause");
	return 0;
}

CreateMutex 互斥锁实现线程同步:

使用互斥锁可以实现单位时间内,只允许一个线程拥有对共享资源的独占,从而实现了互不冲突的线程同步。

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

using namespace std;
HANDLE hMutex = NULL;   // 创建互斥锁

// 线程函数
DWORD WINAPI Func(LPVOID lpParamter)
{
	for (int x = 0; x < 10; x++)
	{
		// 请求获得一个互斥锁
		WaitForSingleObject(hMutex, INFINITE);
		cout << "thread func" << endl;
		// 释放互斥锁
		ReleaseMutex(hMutex);
	}
	return 0;
}

int main(int argc,char * argv[])
{
	HANDLE hThread = CreateThread(NULL, 0, Func, NULL, 0, NULL);

	hMutex = CreateMutex(NULL, FALSE, "lyshark");
	CloseHandle(hThread);

	for (int x = 0; x < 10; x++)
	{
		// 请求获得一个互斥锁
		WaitForSingleObject(hMutex, INFINITE);
		cout << "main thread" << endl;

		// 释放互斥锁
		ReleaseMutex(hMutex);
	}
	system("pause");
	return 0;
}

通过互斥锁,同步执行两个线程函数。

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

using namespace std;
HANDLE hMutex = NULL;   // 创建互斥锁
#define NUM_THREAD 50

// 线程函数1
DWORD WINAPI FuncA(LPVOID lpParamter)
{
	for (int x = 0; x < 10; x++)
	{
		// 请求获得一个互斥锁
		WaitForSingleObject(hMutex, INFINITE);
		cout << "this is thread func A" << endl;
		// 释放互斥锁
		ReleaseMutex(hMutex);
	}
	return 0;
}

// 线程函数2
DWORD WINAPI FuncB(LPVOID lpParamter)
{
	for (int x = 0; x < 10; x++)
	{
		// 请求获得一个互斥锁
		WaitForSingleObject(hMutex, INFINITE);
		cout << "this is thread func B" << endl;
		// 释放互斥锁
		ReleaseMutex(hMutex);
	}
	return 0;
}

int main(int argc, char * argv[])
{

	// 用来存储线程函数的句柄
	HANDLE tHandle[NUM_THREAD];

	// /创建互斥量,此时为signaled状态
	hMutex = CreateMutex(NULL, FALSE, "lyshark");

	for (int x = 0; x < NUM_THREAD; x++)
	{
		if (x % 2)
		{
			tHandle[x] = CreateThread(NULL, 0, FuncA, NULL, 0, NULL);
		}
		else
		{
			tHandle[x] = CreateThread(NULL, 0, FuncB, NULL, 0, NULL);
		}
	}

	// 等待所有线程函数执行完毕
	WaitForMultipleObjects(NUM_THREAD, tHandle, TRUE, INFINITE);

	// 销毁互斥对象
	CloseHandle(hMutex);

	system("pause");
	return 0;
}

通过临界区实现线程同步:

临界区与互斥锁差不多,临界区使用时会创建CRITICAL_SECTION临界区对象,同样相当于一把钥匙,线程函数执行结束自动上交,如下是临界区函数的定义原型。

//初始化函数原型
VOID InitializeCriticalSection(
  LPCRITICAL_SECTION lpCriticalSection
);

//销毁函数原型
VOID DeleteCriticalSection(
  LPCRITICAL_SECTION lpCriticalSection
);

//获取
VOID EnterCriticalSection(
  LPCRITICAL_SECTION lpCriticalSection
);

//释放
VOID LeaveCriticalSection(
  LPCRITICAL_SECTION lpCriticalSection
);

这一次我们不适用互斥体,使用临界区实现线程同步,结果与互斥体完全一致,看个人喜好。

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

using namespace std;
CRITICAL_SECTION cs;         // 全局定义临界区对象
#define NUM_THREAD 50

// 线程函数
DWORD WINAPI FuncA(LPVOID lpParamter)
{
	for (int x = 0; x < 10; x++)
	{
		//进入临界区
		EnterCriticalSection(&cs);

		cout << "this is thread func A" << endl;

		//离开临界区
		LeaveCriticalSection(&cs);

	}
	return 0;
}

int main(int argc, char * argv[])
{
	// 用来存储线程函数的句柄
	HANDLE tHandle[NUM_THREAD];

	//初始化临界区
	InitializeCriticalSection(&cs);

	for (int x = 0; x < NUM_THREAD; x++)
	{
		tHandle[x] = CreateThread(NULL, 0, FuncA, NULL, 0, NULL);
	}

	// 等待所有线程函数执行完毕
	WaitForMultipleObjects(NUM_THREAD, tHandle, TRUE, INFINITE);

	//释放临界区
	DeleteCriticalSection(&cs);

	system("pause");
	return 0;
}

Semaphore 基于信号实现线程同步:

通过定义一个信号,初始化信号为0,利用信号量值为0时进入non-signaled状态,大于0时进入signaled状态的特性即可实现线程同步。

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

using namespace std;

static HANDLE SemaphoreOne;
static HANDLE SemaphoreTwo;

// 线程函数1
DWORD WINAPI FuncA(LPVOID lpParamter)
{
	for (int x = 0; x < 10; x++)
	{
		// 临界区开始时设置 signaled 状态
		WaitForSingleObject(SemaphoreOne, INFINITE);

		cout << "this is thread func A" << endl;

		// 临界区结束则设置为 non-signaled 状态
		ReleaseSemaphore(SemaphoreOne, 1, NULL);
	}
	return 0;
}

// 线程函数2
DWORD WINAPI FuncB(LPVOID lpParamter)
{
	for (int x = 0; x < 10; x++)
	{
		// 临界区开始时设置 signaled 状态
		WaitForSingleObject(SemaphoreTwo, INFINITE);

		cout << "this is thread func B" << endl;

		// 临界区结束则设置为 non-signaled 状态
		ReleaseSemaphore(SemaphoreTwo, 1, NULL);
	}
	return 0;
}

int main(int argc, char * argv[])
{
	// 用来存储线程函数的句柄
	HANDLE hThreadA, hThreadB;

	// 创建信号量对象,并且设置为0进入non-signaled状态
	SemaphoreOne = CreateSemaphore(NULL, 0, 1, NULL);

	// 创建信号量对象,并且设置为1进入signaled状态
	SemaphoreTwo = CreateSemaphore(NULL, 1, 1, NULL);       // 先执行这一个线程函数

	hThreadA = CreateThread(NULL, 0, FuncA, NULL,0, NULL);
	hThreadB = CreateThread(NULL, 0, FuncB, NULL, 0, NULL);

	// 等待两个线程函数执行完毕
	WaitForSingleObject(hThreadA, INFINITE);
	WaitForSingleObject(hThreadA, INFINITE);

	// 销毁两个线程函数
	CloseHandle(SemaphoreOne);
	CloseHandle(SemaphoreTwo);

	system("pause");
	return 0;
}

上面的一段代码,容易产生死锁现象,即,线程函数B执行完成后,A函数一直处于等待状态。

执行WaitForSingleObject(semTwo, INFINITE);会让线程函数进入类似挂起的状态,当接到ReleaseSemaphore(semOne, 1, NULL);才会恢复执行。

#include <windows.h>
#include <stdio.h>  

static HANDLE semOne,semTwo;
static int num;

// 线程函数A用于接收参书
DWORD WINAPI ReadNumber(LPVOID lpParamter)
{
	int i;
	for (i = 0; i < 5; i++)
	{
		fputs("Input Number: ", stdout);
		//临界区的开始 signaled状态
		WaitForSingleObject(semTwo, INFINITE);

		scanf("%d", &num);

		//临界区的结束 non-signaled状态
		ReleaseSemaphore(semOne, 1, NULL);
	}
	return 0;
}

// 线程函数B: 用户接受参数后完成计算
DWORD WINAPI Check(LPVOID lpParamter)
{
	int sum = 0, i;
	for (i = 0; i < 5; i++)
	{
		//临界区的开始 non-signaled状态
		WaitForSingleObject(semOne, INFINITE);
		sum += num;
		//临界区的结束 signaled状态
		ReleaseSemaphore(semTwo, 1, NULL);
	}
	printf("The Number IS: %d \n", sum);
	return 0;
}

int main(int argc, char *argv[])
{
	HANDLE hThread1, hThread2;

	//创建信号量对象,设置为0进入non-signaled状态
	semOne = CreateSemaphore(NULL, 0, 1, NULL);

	//创建信号量对象,设置为1进入signaled状态
	semTwo = CreateSemaphore(NULL, 1, 1, NULL);

	hThread1 = CreateThread(NULL, 0, ReadNumber, NULL, 0, NULL);
	hThread2 = CreateThread(NULL, 0, Check, NULL, 0, NULL);

	// 关闭临界区
	WaitForSingleObject(hThread1, INFINITE);
	WaitForSingleObject(hThread2, INFINITE);

	CloseHandle(semOne);
	CloseHandle(semTwo);

	system("pause");
	return 0;
}

CreateEvent 事件对象的同步:

事件对象实现线程同步,与前面的临界区和互斥体有很大的不同,该方法下创建对象时,可以在自动non-signaled状态运行的auto-reset模式,当我们设置好我们需要的参数时,可以直接使用SetEvent(hEvent)设置事件状态,会自动执行线程函数。

#include <windows.h>
#include <stdio.h>
#include <process.h>
#define STR_LEN 100  

// 存储全局字符串
static char str[STR_LEN];

// 设置事件句柄
static HANDLE hEvent;

// 统计字符串中是否存在A
unsigned WINAPI NumberOfA(void *arg)
{
	int cnt = 0;
	// 等待线程对象事件
	WaitForSingleObject(hEvent, INFINITE);
	for (int i = 0; str[i] != 0; i++)
	{
		if (str[i] == 'A')
			cnt++;
	}
	printf("Num of A: %d \n", cnt);
	return 0;
}

// 统计字符串总长度
unsigned WINAPI NumberOfOthers(void *arg)
{
	int cnt = 0;
	// 等待线程对象事件
	WaitForSingleObject(hEvent, INFINITE);
	for (int i = 0; str[i] != 0; i++)
	{
		if (str[i] != 'A')
			cnt++;
	}
	printf("Num of others: %d \n", cnt - 1);
	return 0;
}

int main(int argc, char *argv[])
{
	HANDLE hThread1, hThread2;

	// 以non-signaled创建manual-reset模式的事件对象
	// 该对象创建后不会被立即执行,只有我们设置状态为Signaled时才会继续
	hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

	hThread1 = (HANDLE)_beginthreadex(NULL, 0, NumberOfA, NULL, 0, NULL);
	hThread2 = (HANDLE)_beginthreadex(NULL, 0, NumberOfOthers, NULL, 0, NULL);

	fputs("Input string: ", stdout);
	fgets(str, STR_LEN, stdin);

	// 字符串读入完毕后,将事件句柄改为signaled状态
	SetEvent(hEvent);

	WaitForSingleObject(hThread1, INFINITE);
	WaitForSingleObject(hThread2, INFINITE);

	//non-signaled 如果不更改,对象继续停留在signaled
	ResetEvent(hEvent);

	CloseHandle(hEvent);

	system("pause");
	return 0;
}

线程函数传递单个参数:

线程函数中的定义中LPVOID允许传递一个参数,只需要在县城函数中接收并强转(int)(LPVOID)port即可。

#include <stdio.h>
#include <Windows.h>

// 线程函数接收一个参数
DWORD WINAPI ScanThread(LPVOID port)
{
	// 将参数强制转化为需要的类型
	int Port = (int)(LPVOID)port;
	printf("[+] 端口: %5d \n", port);
	return 1;
}

int main(int argc, char* argv[])
{
	HANDLE handle;

	for (int port = 0; port < 100; port++)
	{
		handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ScanThread, (LPVOID)port, 0, 0);
	}
	WaitForSingleObject(handle, INFINITE);

	system("pause");
	return 0;
}

线程函数传递多参数:

如果想在线程函数中传递多个参数,则需要传递一个结构指针,通过线程函数内部强转为结构类型后,取值,这个案例花费了我一些时间,网上也没找到合适的解决方法,或找到的都是歪瓜裂枣瞎转的东西,最后还是自己研究了一下写了一个没为题的。

其主要是线程函数中调用的参数会与下一个线程函数结构相冲突,解决的办法时在每次进入线程函数时,自己拷贝一份,每个人使用自己的那一份,才可以避免此类事件的发生,同时最好配合线程同步一起使用,如下时线程扫描器的部分代码片段。

#include <stdio.h>
#include <windows.h>

typedef struct _THREAD_PARAM
{
	char *HostAddr;             // 扫描主机
	DWORD dwStartPort;          // 端口号
}THREAD_PARAM;

// 这个扫描线程函数
DWORD WINAPI ScanThread(LPVOID lpParam)
{
	// 拷贝传递来的扫描参数
	THREAD_PARAM ScanParam = { 0 };

	// 这一步很重要,如不拷贝,则会发生重复赋值现象,导致扫描端口一直都是一个。
	// 坑死人的玩意,一开始我始终没有发现这个问题。sb玩意!!
	MoveMemory(&ScanParam, lpParam, sizeof(THREAD_PARAM));

	printf("地址: %-16s --> 端口: %-5d 状态: [Open] \n", ScanParam.HostAddr, ScanParam.dwStartPort);
	return 0;
}

int main(int argc, char *argv[])
{
	THREAD_PARAM ThreadParam = { 0 };
	ThreadParam.HostAddr = "192.168.1.10";

	for (DWORD port = 1; port < 100; port++)
	{
		ThreadParam.dwStartPort = port;
		HANDLE hThread = CreateThread(NULL, 0, ScanThread, (LPVOID)&ThreadParam, 0, NULL);
		WaitForSingleObject(hThread, INFINITE);
	}

	system("pause");
	return 0;
}

文章出处:https://www.cnblogs.com/lyshark

以上就是C++ 如何实现多线程与线程同步的详细内容,更多关于C++ 实现多线程与线程同步的资料请关注我们其它相关文章!

(0)

相关推荐

  • C++11中多线程编程-std::async的深入讲解

    前言 C++11中提供了异步线程接口std::async,std::async是异步编程的高级封装,相对于直接使用std::thread,std::async的优势在于: 1.std::async会自动创建线程去调用线程函数,相对于低层次的std::thread,使用起来非常方便: 2.std::async返回std::future对象,通过返回的std::future对象我们可以非常方便的获取到线程函数的返回结果: 3.std::async提供了线程的创建策略,可以指定同步或者异步的方式去创建

  • c++11新特性多线程操作实战

    c++11多线程操作 线程 thread int main() { thread t1(Test1); t1.join(); thread t2(Test2); t2.join(); thread t3 = t1; thread t4(t1); thread t5 = std::move(t1); thread t6(std::move(t1)); return 0; } t3,t4创建失败,因为thread的拷贝构造和赋值运算符重载的原型是: thread(const thread&) = d

  • 详解C++11 线程休眠函数

    C++ 11之前并未提供专门的休眠函数.c语言的sleep.usleep其实都是系统提供的函数,不同的系统函数的功能还有些差异. 在Windows系统中,sleep的参数是毫秒. sleep(2*1000); //sleep for 2 seconds 在类Unix系统中,sleep()函数的单位是秒. sleep(2); //sleep for 2 seconds 从C++11开始,中C++标准库提供了专门的线程休眠函数,使得你的代码可以独立于不同的平台. std::this_thread::

  • c++ 如何实现线程注入

    简单编写DLL文件: #include <Windows.h> extern "C" __declspec(dllexport) void MsgBox(LPCWSTR szMsg, LPCWSTR Title) { MessageBox(NULL, szMsg, Title, MB_OK); } BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { swit

  • 详解C++11中的线程锁和条件变量

    线程 std::thread类, 位于<thread>头文件,实现了线程操作.std::thread可以和普通函数和 lambda 表达式搭配使用.它还允许向线程的执行函数传递任意多参数. #include <thread> void func() { // do some work } int main() { std::thread t(func); t.join(); return 0; } 上面的例子中,t是一个线程实例,函数func()在该线程运行.调用join()函数是

  • 浅谈c++11线程的互斥量

    为什么需要互斥量 在多任务操作系统中,同时运行的多个任务可能都需要使用同一种资源.这个过程有点类似于,公司部门里,我在使用着打印机打印东西的同时(还没有打印完),别人刚好也在此刻使用打印机打印东西,如果不做任何处理的话,打印出来的东西肯定是错乱的. #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #include <chrono> #include <thread>

  • c++11 多线程编程——如何实现线程安全队列

    线程安全队列的接口文件如下: #include <memory> template<typename T> class threadsafe_queue { public: threadsafe_queue(); threadsafe_queue(const threadsafe_queue&); threadsafe_queue& operator=(const threadsafe_queue&) = delete; void push(T new_va

  • C++多线程获取返回值方法详解

    在许多时候,我们会有这样的需求--即我们想要得到线程返回的值.但是在C++11 多线程中我们注意到,std::thread对象会忽略顶层函数的返回值. 那问题来了,我们要怎么获得线程的返回值呢? 我们通过一个例子来说明如何实现这个需求.用多个线程计算(a+b)/ (x+y) 的值 有两种方法,分别是 1. 传统的方法:在线程间共享指针 #include<iostream> #include<thread> #include<mutex> #include<atom

  • C++11用两个线程轮流打印整数的实现方法

    使用C++11标准的的线程语法,用两个线程轮流打印整数,一个线程打印奇数,一个线程打印偶数.可以练习线程的基本操作.线程锁和条件变量等技术.完整代码如下.代码后面附有主要语句的讲解. #include <thread> #include <iostream> #include <mutex> #include <condition_variable> std::mutex data_mutex; std::condition_variable data_va

  • C++11 简单实现线程池的方法

    什么是线程池 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程.每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中.如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙.如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值.超过最大值的线程可以排队,但他们要等到其他线程完成后才启动. 不使用

  • c++11多线程编程之std::async的介绍与实例

    本节讨论下在C++11中怎样使用std::async来执行异步task. C++11中引入了std::async 什么是std::async std::async()是一个接受回调(函数或函数对象)作为参数的函数模板,并有可能异步执行它们. template<class Fn, class... Args> future<typename result_of<Fn(Args...)>::type> async(launch policy, Fn&& fn

随机推荐