07. 进程和线程

进程和线程

进程

  • 进程通常被定义为一个正在运行的程序的实例
  • 是操作系统动态执行的基本单元
  • 在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
  • 进程由两部分组成:
    1. 操作系统用来管理进程的内核对象。也是系统用来存放关于进程的统计信息的地方。
    2. 是地址空间,它包含所有可执行模块或DLL模块的代码和数据。它还包含动态内存分配的空间。如进 程堆、线程堆栈和堆分配空间。

进程基础

  • 进程的活动性不强。若要使进程完成某项操作,它必须拥有一个在它的环境中运行的线程,该线程负责 执行包含在进程的地址空间中的代码。
  • 操作系统就要为每个线程安排一定的CPU时间。它通过一种循环方式为线程提供时间片(称为量程), 造成一种假象,仿佛所有线程都是同时运行的一样。

进程状态

  1. 创建。进程由创建而产生
  2. 就绪。就是获取了除CPU外的所有资源,只要处理器分配(CPU)资源就可以马上执行
  3. 运行。运行就是获得了处理器(CPU)分配的资源,程序开始执行
  4. 阻塞。当程序条件不够的时候,需要等待条件满足。比如I/O操作时。
  5. 终止。进程的终止也要通过两个步骤:首先,是等待操作系统进行善后处理,最后将其PCB(进程控 制块)清零,并将PCB空间返还给操作系统。

进程工作的机制

  • 之前学习过有入口函数,操作系统实际上并不调用这些,而是调用C/C++的启动函数。
  • main mainCRTStartup
  • wmain wmainCRTStartup
  • Winmain WinMainCRTStartup
  • wWinmain wWinMainCRTStartup

进程示例:抢票

  1. 定义窗口特性和识别信息
STARTUPINFOW si;//新进程的窗口特性
PROCESS_INFORMATION pi;//新进程的识别信息
  1. 定义临界区
CRITICAL_SECTION g_cs;
  1. 初始化临界区
InitializeCriticalSection(&g_cs);
  1. 使用临界区
  2. 离开临界区,注意:break之前也要离开,否则break不经过if外的离开
void fun_sell(LPVOID v)
{
	while (true)
	{
		EnterCriticalSection(&g_cs);//使用

		if (piao > 0)
		{
			printf("第%d个网上平台,卖出第%d张票\n", (int)v, 51 - piao);
			piao--;

		}
		else
		{
			LeaveCriticalSection(&g_cs);//离开
			break;
		}
		LeaveCriticalSection(&g_cs);//离开
		Sleep(10);
	}
}
  1. 释放临界区
DeleteCriticalSection(&g_cs);

线程

  • 线程是进程的一部分,总是在某个进程之内被创建,它是进程的某一个执行序列(函数)

  • 也是由两部分组成:

    1. 内核对象。操作系统用它来对线程实施管理。内核对象也是系统用来存放线程统计信息的地方;
    2. 线程堆栈。用于维护线程在执行代码时需要的所有函数参数和局部变量。
  • 线程总是在某个进程环境中创建的,而且它的整个寿命期都在该进程中。

  • 线程需要的开销比进程少,所以应该设法用增加线程来解决编程问题,要避免创建新的进程。

  • 进程和线程:可以理解为地主和长工的关系。进程会默认带一个主线程

  • 进程不活跃,线程是活跃的。但进程一旦死亡,进程里面的所有线程都会死亡

线程通讯

  • 线程需要两种情况下进行通讯:
    1. 如果有多个线程访问共享资源而不能够让这个共享资源被破坏
    2. 当一个线程将某个任务完成之后,通知另一个线程接着去完成下一任务

线程示例

  1. 定义互斥
HANDLE g_hMutex;
  1. 初始化互斥
g_hMutex = CreateMutex(nullptr, false, nullptr);
  1. 等待互斥
  2. 重置互斥
void fun_sell(LPVOID v)
{
	while (true)
	{
		WaitForSingleObject(g_hMutex, INFINITE);//等待

		if (piao > 0)
		{
			printf("第%d个网上平台,卖出第%d张票\n", (int)v, 51 - piao);
			piao--;

		}
		else
		{
			ReleaseMutex(g_hMutex);//重置
			break;
		}
		ReleaseMutex(g_hMutex);//重置
		Sleep(10);
	}
}
  1. 关闭互斥(相关操作均为handle)
CloseHandle(g_hMutex);

注意

要做离开和重置操作,sleep不能解决问题,多线程一起工作会确认到同一个状态上,然后同时对这一状态进行操作,同一张票会被购买两次!

信号量

  • 信号量是一个内核对象,可用来管理大量有限的系统资源。
  • 用于资源计数 信号量的使用规则:
    1. 当前资源数量大于0,可以去使用资源,发出信号
    2. 当前资源数量是0,没有可以去使用的资源,不发出信号
    3. 当前资源数量不允许为负数
    4. 当前资源数量不能大于最大的资源数量

信号量示例

  1. 在导头文件之后,定义事件句柄
//信号量 1、信号量对象句柄的定义
HANDLE g_hSemaphore;
  1. 在入口函数最前面进行句柄初始化
//信号量 2、信号量的初始化
//第2,3个参数是借助信号量的特点,给出1,初始化的数量为1,最大数也为1,如果有线程占用了,初始数
量为--
g_hSemaphore = CreateSemaphore(nullptr, 1, 1, nullptr);
  1. 在两个while(true)后面都上加入等待通知
//信号量 3、等待通知
WaitForSingleObject(g_hSemaphore, INFINITE);
  1. 在两个else语句区块内下面的第一句加上:重置信号量
//信号量 4、重置信号量
//第2个参数表示重置当前的资源数量
ReleaseSemaphore(g_hSemaphore, 1, 0);
  1. 关闭信号量
//信号量 5、关闭信号量
CloseHandle(g_hSemaphore);

线程间互相通知

  • 某个线程完成工作,另一个线程需要接着运行,用 GetExitCodeThread 去得到线程退出码,得到某一个 线程的退出码,另一个线程就能往下执行。
//互斥解决
DWORD t1, t2, t3, t4;
while (1)
{
GetExitCodeThread(handle1, &t1);//得到线程的退出码
GetExitCodeThread(handle2, &t2);
GetExitCodeThread(handle3, &t3);
GetExitCodeThread(handle4, &t4);
//判断线程退出码如果是STILL_ACTIVE,表示线程仍然活跃,非STILL_ACTIVE,表示线程死亡
if (t1 != STILL_ACTIVE && t2 != STILL_ACTIVE && t3 != STILL_ACTIVE && t4 !=
STILL_ACTIVE)
break;
}
posted @ 2022-06-22 21:34  Quirkygbl  阅读(47)  评论(0编辑  收藏  举报