SysV 信号量线程线程弹跳

SysV semaphore thread thread bouncing

提问人:Edison von Myosotis 提问时间:11/6/2023 最后编辑:Edison von Myosotis 更新时间:11/8/2023 访问量:88

问:

我有一个类似于 Java 和 .NET 中的监视器的监视器类。它可以比互斥锁和条件变量的简单组合更有效。因此,我有一个信号量,它允许输入先前锁定的关键部分,以及一个信号量,用于通知正在等待慢速竞争路径的线程。如果线程正在等待,则在两个信号量上都在等待。
在 Win32 中,两个信号量是分开的,在 Linux 上,我使用设置为允许 sth 的 SysV 信号量。就像 Win32 上的 WaitForMultipleObjects() 一样,等待监视器的“互斥锁”部分被解锁,以及通知部分被单独解锁。
在上面的代码中,我通过两个信号量集来模拟这一点(不使用 semctl() 进行初始化,因为这对 Linux 来说不是必需的)。线程 ist 生成两次,每个线程首先等待两个信号量发出信号,并向相反线程的信号量集的两个信号量发出信号。等待部分类似于我的监视器类,但 singalling 部分同时向集合的两个信号量发出信号,而我在执行 notify() 和离开“互斥锁”保护部分时分别向它们发出信号;因此,对第一组进行单独初始化以检查这是否有效。
但不幸的是,这两个线程都停在第一个 semop 并永远沉睡。你能告诉我我犯了什么错误吗?代码是 C++20,以使其尽可能简短。

#include <thread>
#include <initializer_list>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/stat.h>

using namespace std;

int main()
{
    int
        setA = semget( IPC_PRIVATE, 2, S_IRUSR | S_IWUSR ),
        setB = semget( IPC_PRIVATE, 2, S_IRUSR | S_IWUSR );
    if( setA == -1 || setB == -1 )
        return EXIT_FAILURE;
    static auto xSemop = []( int set, std::initializer_list<sembuf> sems )
    {
        int ret;
        while( (ret = ::semop( set, const_cast<sembuf *>(sems.begin()), sems.size() )) == EINTR );
        return ret;
    };
    if( xSemop( setA, { { 0, 1, 0 } } ) || xSemop( setA, { { 1, 1, 0 } } ) )
        return EXIT_FAILURE;
    constexpr size_t ROUNDS = 1'000'000;
    auto thr = []( int mySet, int yourSet )
    {
        for( size_t r = ROUNDS; r--; )
            if( xSemop( mySet, { { 0, -1, 0 }, { 1, -1, 0 } } )
                || xSemop( yourSet, { { 0, 1, 0 }, { 1, 1, 0 } } ) )
                terminate();
    };
    jthread
        thrA( thr, setA, setB ),
        thrB( thr, setB, setA );
}
Linux 多线程 信号量 sysv-ipc

评论

0赞 Andrew Henle 11/6/2023
代码是 C++20,以使其尽可能简短。好吧,这对你来说效果不佳。您知道传递给每个调用的参数吗?semop()
0赞 Edison von Myosotis 11/7/2023
@AndrewHenle:当然,initializer-list<sembuf>s 使代码更短。否则,我将不得不手动烹饪 sembuf-arrays。由于初始值设定项列表与传递给 C 数组初始化的初始化相同,因此代码甚至应该可供 C 开发人员读取。使用 C 语言,这肯定是代码的三倍。
0赞 Shawn 11/7/2023
该 hack 的 C 版本是 fwiw。xSemopsemop(1, (struct sembuf[]){ { 0, 1, 0 } }, 1)
0赞 Edison von Myosotis 11/7/2023
@Shawn:我在 C++ 年测试了这一点,因为我第一次看到这个临时数组创建。令我惊讶的是,这在 C++ 中不起作用。但感谢您的提示;也许我将来有一些 C 工作可以使用它。
0赞 Shawn 11/8/2023
C++ 没有像 C 这样的复合文字,没有。

答:

0赞 Edison von Myosotis 11/8/2023 #1

我明白了:如果我增加信号量的计数器,我必须在 sembuf::sem_flg 上设置IPC_NOWAIT,否则我认为线程会等到另一个服务员在集合中减少这个 sempahore。我不知道这应该有什么好处,但我可以忍受。