关于进程与线程的同步和线程的那点问题

前言

我感觉看了一圈面经后发现,我对进程与线程还存在着认识上的漏洞,这次开个帖子再讲清楚这个东西。

进程同步

我整理了三种方式,其中临界区我就不写了。
进程同步的三种方式是:
1.semaphore
2.mutex-spinlock
3.event

线程同步

1.临界区
2.semaphore
3.mutex-spinlock
4.event
5.condition_variable

volatile线程安全么

当然不!

线程状态转移表与条件

这个还有点意思。
线程分为5个状态,我给你看一张表你就全部都明白了。
在这里插入图片描述
这个等待队列和锁池其实是有点意思的。我一开始将等待队列理解成阻塞状态的ready_list,但是后来我想想,觉得不对。
Java里为锁搞了两个池子,一个是锁池,另一个是等待池子。当一堆线程在竞争锁时,成功的人就继续执行,失败的人就进入锁池。当成功者执行完成并准备归还锁时,会唤醒一个锁池中的锁。
成功者也可以主动调用wait操作,把自己挂到等待池子中,同时唤醒锁池的一个人。直到有人主动的notify_one或者是notify_all,才会将等待池子中的人放到锁池中。
ps:等待池中的线程被notify()或者notifyAll()方法唤醒进入到锁池,最后竞争到了锁并且进入了Runnable状态的话,会从wait现场恢复,执行wait()方法之后的代码。
C++的mutex只有一个池子,用来存放那些竞争失败的人,所以说,竞争mutex失败的人是在mutex的池子里挂起的。
C++还有个condition_variable,叫条件变量,和Java说的那种方式非常相似,可以wait操作,notify和notify_all操作,我想这个东西的实现底层应该是有两个池子。

C++11 condition_varibale

这个东西我想大家肯定都不陌生,条件变量里有wait操作,notify_one操作和notify_all操作。有的人肯定想问,为啥有了mutex还要这个condition_variable呢?其实这俩在功能上还是有区别的。条件变量更多的是实现一种“等待–唤醒”逻辑。
至于这三个函数怎么用,请参考上面的锁池和等待池子。

为啥条件变量的操作需要锁定一个mutex呢?

我在看mutex的源码时,看到他有一个condition_variable的友元类,我当时就觉得这俩家伙可能有点不正当关系。
查看一下条件变量的使用范例你也能发现,人家写了俩全局参数(确切的说是仨),先看看怎么用:

// C++ 11.
std::unique_lock<std::mutex> lk(mutex);
cond.wait(lk, []{return ready;});  //Equivalent to: while (!pred()) wait(lock);
//...
//cond.notify_one();

那么咱们回到问题上:为什么要有mutex呢?其实很简单,假如没有mutex,那么当线程A的条件变量判断自己的条件到了,可以把自己wait了的时候,它正准备把自己放到条件变量的等待池子中(还没放进去),这个时候被B打断,开始执行B了。B看了看,哦,B认为根据这个条件来看,等待队列里肯定有人,我这样吧,我把条件改了,然后把A从等待池子里唤醒,哎嘿嘿不错。可是实际上A还没进去呢。这样,A很可能就一直呆在等待池子里了。
那为啥要搞个while循环呢???

spurious wakeup

虚假唤醒了解一下。这个非常好理解,假设有A1 A2和B这三个线程。首先A1一直在干活,直到把条件干到true,然后就wait了自己。接着,B从锁池中唤醒,然后开始干活,就把条件干成false了,之后他notify了所有人。A1和A2都被唤醒了,不过A2抢到了,而A1呆在锁池。A2干完活了就直接把条件干成true就把自己wait了。如果不加loop循环判断,那么A1出来就觉得自己的条件现在肯定不是true,那肯定不对啊,A2刚刚给干成true了!

扫描二维码关注公众号,回复: 11380559 查看本文章

如何获取线程状态

  1. GetExitCodeThread可以判断线程是否还活着
  2. WaitForSingleObject(hThread, INFINITE);//等待直到线程退出

线程常用方法比较

1.sleep这个就很简单,先睡一段时间,然后定时器把它放到就绪队列中继续参加调度(sleep不会释放锁)
2.yield更简单,就是把自己剩下的CPU时间也释放掉,然后把自己放到就绪队列中(这个不会blocked)
3.join当使用join()函数时,主调线程(main函数里有一个主调线程)阻塞,等待被调线程终止,然后主调线程回收被调线程资源,并继续运行;上面这段话的意思就是,使用join(),线程运行完,main函数才能结束。
4.detach当使用detach()函数时,主调线程继续运行,被调线程驻留后台运行,主调线程无法再取得该被调线程的控制权。当主调线程结束时,由运行时库负责清理与被调线程相关的资源。上面这段话的意思就是,使用detach(),main函数不用等待线程结束才能结束。有时候线程还没运行完,main函数就已经结束了。
5.wait这个说过了
6.notify这个也说过了

猜你喜欢

转载自blog.csdn.net/weixin_44039270/article/details/106879749