内核对象的线程同步,比如互斥量、事件对象、信号量对象不会发生死锁 ,为什么呢

内核对象的线程同步,比如互斥量、事件对象、信号量对象不会发生死锁 ,为什么呢

互斥锁、事件对象、信号量对象等内核对象是为了协调多个线程或进程之间对共享资源的访问而设计的,保证线程之间的同步。它们的设计和实现是旨在避免死锁的。

  • 互斥锁、事件对象、信号量都是为了协调和同步线程间的访问共享资源而设计,它们能够有效地避免死锁的发生。具体来说:
  • 互斥锁:当某个线程获得了互斥锁之后,在使用共享资源之前会一直保持这个锁,其他线程在想要使用这个资源的时候,会被阻塞,等待该互斥锁的解锁。这样就能避免出现死锁的情况。
    事件对象:事件对象通常用于线程之间的通信,该对象有“已触发”和“未触发”两种状态。当某个线程等待事件对象上的信号时,如果事件对象不处于“已触发”状态,该线程就会被阻塞,直到事件被触发。这样就能够有效避免死锁的发生。

  • 信号量对象:信号量是一个计数器,它用于协调多个线程对共享资源的访问。当计数器等于0时,线程就会被阻塞。当另一个线程释放信号量时,原先被阻塞的线程就会得到信号量。通过这种方式,信号量可以有效地避免死锁的发生。
    因此,互斥锁、事件对象、信号量等内核对象是专门为线程同步和协作而设计的,它们的设计和实现基于防止死锁的前提。

  • 你可以参考这篇文章,虽然是Java的,但是原理互通,方便你理解:【JUC并发】2. 不可不说的“锁”事,种类繁多,如何一一突破?

  • 这篇博客: 数据结构笔记(图:最短路径、事件时间、关键路径、拓扑序列)中的 五、事件发生时间 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • (一)事件的最早发生时间ve[k]
    ve[k]是指从始点开始到顶点vk的最大路径长度。这个长度决定了所有从顶点vk发出的活动能够开工的最早时间。
    ve[k]的计算:

    q.push(0);//源点事件入队
    	for(j=0;j<vertexnum;j++)	{  //初始化每个事件最早发生时间
    		ve[j]=0;	visit[j]=0;	}
    	visit[0]=1;	
         while(!q.empty())	{		
    		i=q.front();       //利用标准模板库中的队列实现
    		q.pop();
    		for(j=0;j<vertexnum;j++){//计算i的邻接点的ve
    			if(adjlist[i][j]!=9999 && ve[i]+adjlist[i][j]>ve[j] ){
    				ve[j]=ve[i]+adjlist[i][j];
    				if(!visit[j])   //如果j没有被访问过,顶点j入队
    					q.push(j);
    				visit[j]=1;
    			}
    		}
    	}
    

    (二)事件的最迟发生时间vl[k]
    vl[k]是指在不推迟整个工期的前提下,事件vk允许的最晚发生时间

     q.push(vertexnum-1);
    	for(j=0;j<vertexnum;j++)	{
    		vl[j]=ve[vertexnum-1];	visit[j]=0;	}
        while(!q.empty())	{
    		i=q.front();
    		q.pop();
    		for(j=0;j<vertexnum;j++)	{
    			if(adjlist[j][i]!=9999 && vl[i]-adjlist[j][i]<vl[j] ){
    				vl[j]=vl[i]-adjlist[j][i];
    				if(!visit[j])
    					q.push(j);
    				visit[j]=1;
    			}
    		}
    	}
    

    (三)活动的最早开始时间e[i] :
    若活动ai是由弧<vk , vj>表示,则活动ai的最早开始时间应等于事件vk的最早发生时间。因此,有:
    e[i]=ve[k]
    for(i=0;i<e;i++)
    {
    edge[i].e=ve[edge[i].from];
    }
    (四)活动的最晚开始时间l[i]
    活动ai的最晚开始时间是指,在不推迟整个工期的前提下, ai必须开始的最晚时间。
    若ai由弧<vk,vj>表示,
    则ai的最晚开始时间要保证事件vj的最迟发生时间不拖后。
    因此,有:
    l[i]=vl[j]-len<vk, vj>

    for(i=0;i<e;i++)
     {
     	edge[i].e=ve[edge[i].from];
     	edge[i].l=vl[edge[i].to]-adjlist[edge[i].from][edge[i].to];
     }
    
  • 以下回答由chatgpt基于相关博客总结生成:

    对于这个问题,和参考资料中的内容并没有太多关联,以下是解答:

    使用内核对象进行线程同步时,比如互斥量、事件对象、信号量对象,不会出现死锁的情况是因为其内部都通过操作系统提供的机制来进行管理。例如互斥量会通过操作系统提供的互斥量机制来保证同一时刻只有一个线程可以访问共享资源,其他线程需要等待。当进入临界区的线程执行完毕,释放互斥量之后,其他等待线程才可以继续竞争访问共享资源的机会。这个机制会保证互斥量的锁定和释放是原子性的,不会出现死锁。

    再举个例子,如果我们创建了一个事件对象,当事件被触发时,等待该事件的线程将被唤醒。操作系统会管理事件对象的等待队列,保证唤醒的线程正是等待该事件的线程。

    对于信号量对象,当一个线程等待一个信号量时,如果该信号量的计数器不为0,则允许进入,同时将信号量计数器减1,保证只有有限个线程可以同时访问共享资源。如果信号量对象的计数器为0,则该线程被阻塞,直至其他线程释放该信号量。因此通过信号量对象可以实现进程/线程间的同步和互斥操作。

    因此,通过使用操作系统提供的内核对象,可以保证线程同步的正确性和稳定性,不会出现死锁的情况。