关于线程挂起和唤醒的一个设计问题

时间:2022-02-02 18:06:45
背景:实现一个用于windows环境的库。在这个库里面,我想要为外界提供一个阻塞函数,这里记为 void func();

原本的设计方案:
            这个函数在被调用之后,会向我自管理的消息队列(这里记为msg_queue)投递一个专属的消息。随后,调用线程要进入一个等待状态。当另一个处理消息队列的循环过程在检查到这个消息满足条件时,会触发响应的处理,完成以后需要唤醒调用线程,使其继续执行下去。

问题:如何控制这个函数,使其按需要挂起、唤醒调用它的线程。

我目前的设计:
       如下代码所示

       void func()
      {
                 HANDLE hEvent = CreateEvent();
                 my_SendMessage();    //----这个函数会将hEvent的引用内容放入消息队列里
                 WaitForSingleObject(hEvent,....);
                 
                 ........
                 CloseHandle(hEvent);
      }
    
       然后,消息队列处理之后,会在那边SetEvent(hEvent);

      我的问题:
                        这个函数有可能被频繁调用。有没有办法,可以不要每次调用都来一个新的信号量的创建-销毁过程。毕竟是创建内核对象。而且感觉也很不优雅。

                        我想的另一个替代办法是维持一个全局的 线程-信号量关联表。这样每次调用func后,获取当前的线程ID,然后查询,该线程是否已经设置了我需要的信号量,如果有,则继续使用,无的话,创建。线程退出的时候销毁。不过,这个方案的开销搞不好更多。

                         麻烦各位朋友给予建议。谢谢。

7 个解决方案

#1


直接等待event就可以了啊

#2


可不可以换个思考角度:为外界提供一个等待一个或者多个事件的函数,这里记为 void func(……);如果事先定义的这些事件没有发生函数就进入阻塞等待状态,直到其他线程把这些事件都准备好了,那么这个阻塞的线程就自然苏醒过来了,这样就省去你的个消息队列了,而只要事先把需要用到的事件定义好就可以了。

#3


WaitForSingleEvent/SetEvent
通过Event来进行同步

#4


两个方案
第一种,既然是频繁调用的函数,你就为每个调用线程固定创建一个Event就行,不用每次临时创建并Close
可以考虑将句柄放在TLS里

第二种,调用线程投递消息后,自我挂起SuspendThread,然后由处理线程完成后调用ResumeThread 唤醒

#5


引用 4 楼 vocanicy 的回复:
两个方案
第一种,既然是频繁调用的函数,你就为每个调用线程固定创建一个Event就行,不用每次临时创建并Close
可以考虑将句柄放在TLS里

第二种,调用线程投递消息后,自我挂起SuspendThread,然后由处理线程完成后调用ResumeThread 唤醒


这几天把代码写完了,测试效果不错。

看了一下,采用了和您的建议差不多的办法,唯一的区别是我把这个event和资源绑定在了一起,

也就是,把event设置在了连接的数据队列里,这样每次调用func的时候,如果存在资源,则直接处理,如果不存在资源,则自行挂起,等待另一端线程唤醒。而另一端的线程只要有资源投递的动作,就自动设置event。

那么问题解决了。抱歉,我现在看了一下我的帖子,感觉没有完全表述清楚我当初的用意,可能当时没有想到合适的策略。

#6


引用 2 楼 zhdhj 的回复:
可不可以换个思考角度:为外界提供一个等待一个或者多个事件的函数,这里记为 void func(……);如果事先定义的这些事件没有发生函数就进入阻塞等待状态,直到其他线程把这些事件都准备好了,那么这个阻塞的线程就自然苏醒过来了,这样就省去你的个消息队列了,而只要事先把需要用到的事件定义好就可以了。


您的这个建议有点像  nginx的设计呢~

#7


引用 3 楼 oyljerry 的回复:
WaitForSingleEvent/SetEvent
通过Event来进行同步


是的没错,当初考虑的只是如何在代码的哪一部分合理的设置event的位置。

#1


直接等待event就可以了啊

#2


可不可以换个思考角度:为外界提供一个等待一个或者多个事件的函数,这里记为 void func(……);如果事先定义的这些事件没有发生函数就进入阻塞等待状态,直到其他线程把这些事件都准备好了,那么这个阻塞的线程就自然苏醒过来了,这样就省去你的个消息队列了,而只要事先把需要用到的事件定义好就可以了。

#3


WaitForSingleEvent/SetEvent
通过Event来进行同步

#4


两个方案
第一种,既然是频繁调用的函数,你就为每个调用线程固定创建一个Event就行,不用每次临时创建并Close
可以考虑将句柄放在TLS里

第二种,调用线程投递消息后,自我挂起SuspendThread,然后由处理线程完成后调用ResumeThread 唤醒

#5


引用 4 楼 vocanicy 的回复:
两个方案
第一种,既然是频繁调用的函数,你就为每个调用线程固定创建一个Event就行,不用每次临时创建并Close
可以考虑将句柄放在TLS里

第二种,调用线程投递消息后,自我挂起SuspendThread,然后由处理线程完成后调用ResumeThread 唤醒


这几天把代码写完了,测试效果不错。

看了一下,采用了和您的建议差不多的办法,唯一的区别是我把这个event和资源绑定在了一起,

也就是,把event设置在了连接的数据队列里,这样每次调用func的时候,如果存在资源,则直接处理,如果不存在资源,则自行挂起,等待另一端线程唤醒。而另一端的线程只要有资源投递的动作,就自动设置event。

那么问题解决了。抱歉,我现在看了一下我的帖子,感觉没有完全表述清楚我当初的用意,可能当时没有想到合适的策略。

#6


引用 2 楼 zhdhj 的回复:
可不可以换个思考角度:为外界提供一个等待一个或者多个事件的函数,这里记为 void func(……);如果事先定义的这些事件没有发生函数就进入阻塞等待状态,直到其他线程把这些事件都准备好了,那么这个阻塞的线程就自然苏醒过来了,这样就省去你的个消息队列了,而只要事先把需要用到的事件定义好就可以了。


您的这个建议有点像  nginx的设计呢~

#7


引用 3 楼 oyljerry 的回复:
WaitForSingleEvent/SetEvent
通过Event来进行同步


是的没错,当初考虑的只是如何在代码的哪一部分合理的设置event的位置。