windows 多线程

进程/线程

一、程序运行

 1.每个程序运行的时候,操作系统会给这个程序分配一个进程,以32位操作系统为例,就会分配4GB虚拟内存空间(代码段、数据段、堆、栈),将程序的代码加载到代码段,并运行程序,执行程序指令。

二、什么是线程

 1.线程是基于进程的轻量级的调度单元,也是操作系统可以调用独立的最小单元,线程是在一个进程中创建出来的,当一个进程分配出来时,他本身就是一个线程。

 2.一个进程可创建多个线程,这些线程共享进程的代码段、数据段、堆,唯一不共用栈,每个线程都有自己独立的栈,这样每个线程的函数调用与执行都是独立互不影响的。

 3.线程在执行过程中,随时有可能挂起调度出去,所以当两个线程在访问共同资源时要注意数据的同步。

 4.代码段、数据段、堆上的数据每个线程都是共享访问。


线程挂起/休眠

一、线程事件(线程条件)

 1.线程可以通过系统api挂在事件对象上,挂起后线程不会往下执行(不会被调度),只有到事件被触发了,唤醒了挂在这个事件上的所有线程。

二、休眠

 1.线程可以调用休眠函数来挂起,休眠时间结束后唤醒。

三、线程安全

 1.当有多个线程访问同一个共享数据时(全局变量、堆),如果要把这个共享数据设计成线程安全的,则需要使用临界区或互斥量(暂不介绍)。

 2.线程先申请进入临界区,如果临界区已被占用,则挂起线程、等待直到其他线程离开临界区。

 3.进入临界区后可操作该共享数据。

 4.操作完共享数据后,离开临界区,此时能唤醒等待进入临界区的其他线程,其他线程进入临界区后同样操作共享数据,再离开临界区。


创建线程

一、创建一个线程

HANDLE WINAPI CreateThread(

 LPSECURITY_ATTRIBUTESlpThreadAttributes,//线程内核对象安全属性,一般传NULL表示使用默认SIZE_TdwStackSize,//线程栈控件大小,一般传0表示使用默认(1MB)

 LPTHREAD_START_ROUTINElpStartAddress,//新线程锁执行的函数地址

 LPVOIDlpParameter,//传给线程函数的参数

 DWORDdwCreationFlags,//指定标志来控制线程创建,0表示创建后可立即调度,CREATE_SUSPENDED表示线程创建后暂停运行,这样它将无法调度,直到调用ResumeThread()

 LPDWORDlpThreadId//输出线程ID,传NULL表示不需返回线程ID

);


等待线程结束

一、等待内核对象触发 WaitForSingleObject/WaitForMultipleObjects

DWORDWINAPIWaitForSingleObject(

 HANDLE hHandle,//

 DWORDdwMilliseconds//

);

二、获取线程ID函数

 GetCurrentThreadId();


事件操作

一、创建事件

HANDLE CreateEvent(

 LPSECURITY_ATTRIBUTESlpEventAttributes,//表示安全控制,一般传NULL

 BOOL bManualReset,//手动还是自动置位,传FALSE表示自动置位,则对该事件调用WaitForSingleObject()后会自动调用ResetEvent()使事件变成未触发状态

 BOOL bInitialState,//表示事件初始状态,true表示已触发

 LPCTSTRlpName//表示事件的名称,传NULL表示匿名事件

);

二、打开事件

HANDLE OpenEvent(

 DWORDdwDesiredAccess,//表示访问的权限,一般传EVENT_ALL_ACCESS,详细解释可查看相关文档

 BOOLbInheritHandle,//表示事件句柄继承性,一般传TRUE

 LPCTSTRlpName     //名称,不同进程的各线程可以通过名称来确保它们访问的是用一事件

);

三、触发事件

 BOOL SetEvent(HANDLE hEvent);//每次调用后,必有一个或多个处于等待状态下的线程变成可调度状态

四、重置事件

 BOOL SetEvent(HANDLE hEvent); //每次调用后,必有一个或多个处于等待状态下的线程变成可调度状态


临界区

一、  临界区:

 CRITICAL_SECTION 临界区对象;

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

 EnterCriticalSection(); // 请求进入临界区

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


程序代码

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

//全局变量(数据段)
static int global_value = 0;
//

//共用代码段函数
void fun(char* printStr)
{
 printf("%s\n", printStr);
}
//

/*
事件通知/等待:
*线程A,等待线程B完成达到某条件后,才能继续;
*案例:多媒体解码线程,等待输入线程输入数据,有数据后,通知解码线程解码;
*步骤:
1.创建一个事件,线程都可以访问;
2.等待的线程,调用函数来等待时间;
3.触发的线程,当条件满足后触发;
*/
static HANDLE wait_condition = INVALID_HANDLE_VALUE;

/*
线程安全机制:
*代码段、数据段、堆是共享的,所以会存在一个问题,多个线程同时访问一个资源时,由于线程之间随时切换,所以就会导致访问的同一资源可能产生冲突;
*为了防止线程之间资源访问冲突,那么可以加一个临界区的机制;
*临界区保证了共享资源同时只有一个线程在处理;
*另一种术语叫:线程同步/线程同步锁
*步骤:
1.需要请求一个资源时,先进入临界区,如果这个临界区已被占用了,则等待其他线程离开临界区;
2.如果请求成功,则逻辑处理,处理完后离开这个临界区;

*线程死锁
线程A: 进入区1、进入区2 ······ 离开临界区1,离开临界区2
线程B: 进入区2、进入区1 ······ 离开临界区2,离开临界区1

*避免死锁: 用同样的顺序来获得我们的多个锁
线程A: 进入区1、进入区2 ······ 离开临界区2,离开临界区1
线程B: 进入区1、进入区2 ······ 离开临界区2,离开临界区1
*/
CRITICAL_SECTION section;

DWORD WINAPI thread_entry(LPVOID lpThreadParameter)
{
 //线程启动后3秒后触发事件
 Sleep(3000);
 SetEvent(wait_condition);

 while (1)
 {
   fun("sub thread");
   //进入临界区
   EnterCriticalSection(&section);
   //访问共享资源
   global_value = 100;
   //离开临界区
   LeaveCriticalSection(&section);
   printf("global_value:%d\n", global_value);
   Sleep(3000); //3秒
 }
 return 0;
}

int main(int argc, char** argv)
{
 /*
 1.线程句柄: HANDLE;
 2.创建线程API: CreateThread;
 3.属性: NULL;
 4.栈大小: 0;
 5.执行函数地址: thread_entry;
 6.执行函数参数指针: NULL;
 7.标记: 0;
 8.线程id: thread_id;
 */
 DWORD thread_id;
 HANDLE h = CreateThread(NULL, 0, thread_entry, NULL, 0, &thread_id);

 //事件创建
 /*
 1.事件句柄: HANDLE;
 2.创建线程API: CreateEvent;
 3.属性: NULL;
 4.手动重置: FALSE;
 5.初始触发: FALSE;
 6.名称:NULL;
 */
 wait_condition = CreateEvent(NULL,FALSE,FALSE,NULL);

 //等待事件触发
 printf("waiting...\n");
 WaitForSingleObject(wait_condition, INFINITE);

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

 //主线程
 while (1)
 {
   fun("main thread");
   //进入临界区
   EnterCriticalSection(&section);
   //访问共享资源
   global_value = 200;
   //离开临界区
   LeaveCriticalSection(&section);
   printf("global_value:%d\n", global_value);
   Sleep(1700); //1.7秒
 }
 system("pause");
 return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值