C/C++ 实现多线程与线程同步
多线程中的线程同步可以使用,CreateThread,CreateMutex 互斥锁实现线程同步,通过临界区实现线程同步,Semaphore 基于信号实现线程同步,CreateEvent 事件对象的同步,以及线程函数传递单一参数与多个参数的实现方式。
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;
}
C/C++ 实现多线程与线程同步的更多相关文章
- C#多线程之线程同步篇3
在上一篇C#多线程之线程同步篇2中,我们主要学习了AutoResetEvent构造.ManualResetEventSlim构造和CountdownEvent构造,在这一篇中,我们将学习Barrier ...
- C#多线程之线程同步篇2
在上一篇C#多线程之线程同步篇1中,我们主要学习了执行基本的原子操作.使用Mutex构造以及SemaphoreSlim构造,在这一篇中我们主要学习如何使用AutoResetEvent构造.Manual ...
- C#多线程之线程同步篇1
在多线程(线程同步)中,我们将学习多线程中操作共享资源的技术,学习到的知识点如下所示: 执行基本的原子操作 使用Mutex构造 使用SemaphoreSlim构造 使用AutoResetEvent构造 ...
- 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLock
[源码下载] 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLoc ...
- 重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore, CountdownEvent, Barrier, ManualResetEvent, AutoResetEvent
[源码下载] 重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore, CountdownEvent, Barrier, ManualResetEve ...
- IOS 多线程,线程同步的三种方式
本文主要是讲述 IOS 多线程,线程同步的三种方式,更多IOS技术知识,请登陆疯狂软件教育官网. 一般情况下我们使用线程,在多个线程共同访问同一块资源.为保护线程资源的安全和线程访问的正确性. 在IO ...
- 关于Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇高质量的博文)
Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文) 前言:在学习多线程时,遇到了一些问题,这里我将这些问题都分享出来,同时也分享了几篇其他博客主的博客,并且将我个人的理解也分享 ...
- Java:多线程,线程同步,同步锁(Lock)的使用(ReentrantLock、ReentrantReadWriteLock)
关于线程的同步,可以使用synchronized关键字,或者是使用JDK 5中提供的java.util.concurrent.lock包中的Lock对象.本文探讨Lock对象. synchronize ...
- MFC——9.多线程与线程同步
Lesson9:多线程与线程同步 程序.进程和线程是操作系统的重点,在计算机编程中.多线程技术是提高程序性能的重要手段. 本文主要解说操作系统中程序.进程和线程之间的关系,并通过相互排斥对象和事件对象 ...
- Java多线程 3 线程同步
在之前,已经学习到了线程的创建和状态控制,但是每个线程之间几乎都没有什么太大的联系.可是有的时候,可能存在多个线程多同一个数据进行操作,这样,可能就会引用各种奇怪的问题.现在就来学习多线程对数据访问的 ...
随机推荐
- Win网络安全审计
目录 Win进程通信 netstat -nb TCPView 审计登陆历史 security日志 WinLogOnView Win进程通信 netstat -nb 用这个命令就能看到进程和外部的IP连 ...
- 用于功率集成电路应用的600伏、10安、4H-SIC横向单沟道金属氧化物半导体场效应晶体管的演示和分析
用于功率集成电路应用的600伏.10安.4H-碳化硅横向单沟道金属氧化物半导体场效应晶体管的演示和分析 摘要: 本文报道了一个具有大电流处理能力(10 A)的600伏4H-碳化硅横向场效应晶体管的演示 ...
- kubernetes cpu限制参数说明
docker CPU限制参数 Option Description --cpus=<value> Specify how much of the available CPU resourc ...
- GNS3通过“云”连接到虚拟机实验
GNS3通过"云"连接到虚拟机实验并使用wireshark工具对数据分析 观看本文之前注意!!!!! 做这次实验,我所遇到的问题,会全部写在文章结尾,如果读者们遇到问题,可查看. ...
- 02----python入门----基本数据类型
关于数据分类依据 一.数字型(int) Python可以处理任意大小的正负整数,但是实际中跟我们计算机的内存有关,在32位机器上,整数的位数为32位,取值范围为-2**31-2**31-1,在64位系 ...
- 单元测试 - SpringBoot2+Mockito实战
单元测试 - SpringBoot2+Mockito实战 在真实的开发中,我们通常是使用SpringBoot的,目前SpringBoot是v2.4.x的版本(SpringBoot 2.2.2.RELE ...
- 再探循环依赖 → Spring 是如何判定原型循环依赖和构造方法循环依赖的?
开心一刻 一天,侄子和我哥聊天,我坐在旁边听着 侄子:爸爸,你爱我妈妈吗? 哥:这话说的,不爱能有你吗? 侄子:确定有我不是因为荷尔蒙吗? 哥:因为什么荷尔蒙,因为爱情! 侄子:那我妈花点钱,你咋老说 ...
- 3.学习numyp的矩阵
Numpy提供了ndarray来进行矩阵的操作,在Numpy中 矩阵继承于NumPy中的二维数组对象,但是矩阵区别于数组,不可共用数组的运算规律 一.创建矩阵 import numpy as np m ...
- 攻防世界 reverse BabyXor
BabyXor 2019_UNCTF 查壳 脱壳 dump 脱壳后 IDA静态分析 int main_0() { void *v0; // eax int v1; // ST5C_4 char ...
- Git命令太多记不住?有了这个神器,从此告别输入命令行
一 .SourceTree简介 SourceTree 是 Windows 和Mac OS X 下免费的 Git 和 Hg 客户端,拥有可视化界面,容易上手操作.同时它也是Mercurial和Subve ...