多线程.如何平等地分享公共资源

Multithreading. How to equally share common resources?

本文关键字:分享 公共资源 何平 多线程      更新时间:2023-10-16

简单的代码(我知道这是一个非常糟糕的代码,但我只是为了这个例子而做的):

 1 #include <mutex>
 2 #include <iostream>
 3 #include <thread>
 4 
 5 std::mutex mu;
 6 
 7 void myFunc(void) {
 8         for (int i = 0; i < 100; ++i) {
 9                 mu.lock();
 10                 std::cout << "CHILD Thread: " << std::this_thread::get_id() << std::endl;
 11                 mu.unlock();
 12         }
 13 }
 14 
 15 int main()
 16 {
 17         std::thread thr(myFunc);
 18         for (int i = 0; i < 100; ++i) {
 19                 mu.lock();
 20                 std::cout << "MAIN Thread: " << std::this_thread::get_id() << std::endl;
 21                 mu.unlock();
 22         }
 23         thr.join();
 24         return 0;
 25 }

返回此类输出:

1      MAIN Thread: 140581832210240
2      MAIN Thread: 140581832210240
3      MAIN Thread: 140581832210240
4      MAIN Thread: 140581832210240
5      MAIN Thread: 140581832210240
6      MAIN Thread: 140581832210240
7      MAIN Thread: 140581832210240
8      CHILD Thread: 140581814855424
9      CHILD Thread: 140581814855424
10     CHILD Thread: 140581814855424
11     CHILD Thread: 140581814855424
12     CHILD Thread: 140581814855424
13     CHILD Thread: 140581814855424
14     CHILD Thread: 140581814855424
15     CHILD Thread: 140581814855424
16     CHILD Thread: 140581814855424
17     CHILD Thread: 140581814855424
18     MAIN Thread: 140581832210240
19     MAIN Thread: 140581832210240
20     MAIN Thread: 140581832210240
21     MAIN Thread: 140581832210240
22     MAIN Thread: 140581832210240
23     CHILD Thread: 140581814855424
24     CHILD Thread: 140581814855424
25     CHILD Thread: 140581814855424
26     CHILD Thread: 140581814855424
27     CHILD Thread: 140581814855424
28     CHILD Thread: 140581814855424
29     CHILD Thread: 140581814855424
           ....... and so on

正如我所看到的 - 这个输出违背了多线程的意义,因为一个线程必须等待另一个线程很长时间。这个输出应该同时给我子项的cout,主子项的cout,子项的cout,主要的cout等等。我知道互斥锁不负责公平份额的公共资源,但是:谁是?我如何在我的程序中实现它?

谢谢。

编辑:将std::cout放入一个函数中:

10 void common_cout(string msg) {
11         mu.lock();
12         std::cout << msg << std::endl;
13         mu.unlock();
14 }

无济于事。

原始代码在 Windows 中存在相同的问题,但我改用本机 Windows 等效项,并且此 windows 示例按照您期望的方式工作,在两个线程之间交替。ReleaseMutex() 的每个实例都会导致"其他"线程获取互斥锁并运行。main 中的 Sleep(2) 是确保 myFunc 首先启动循环的简单方法。

我还创建了一个包含主线程和两个线程的版本,总共三个线程。循环按顺序进行,因此 Windows 本机互斥锁似乎是按请求的顺序完成的。

对于循环类型的循环或线程和/或进程

之间的常规同步,每个线程或进程使用一个信号量更好,因为任何线程或进程都可以增加(释放/信号)任何信号量。这样做的问题是信号量不是标准线程接口的本机部分,需要互斥锁和条件变量的某种组合来实现等效的信号量。Windows 和 posix 支持本机信号量。

#include <iostream>
#include <windows.h>
static HANDLE mu;                       // handle: mutex
static HANDLE ht1;                      // handle: thread 1
static DWORD  id1;                      // thread 1 id
DWORD WINAPI myFunc(LPVOID) {
    for (int i = 0; i < 20; ++i) {
        WaitForSingleObject(mu, INFINITE);
        std::cout << "child thread: " << i << std::endl;
        ReleaseMutex(mu);
    }
    return 0;
}
int main()
{
    mu = CreateMutex(NULL,TRUE,NULL);   // main owns mutex
    ht1 = CreateThread(NULL, 0, myFunc, 0, 0, &id1);
    Sleep(2);                           // make sure myFunc running
    ReleaseMutex(mu);                   // release mutex
    for (int i = 0; i < 20; ++i) {
        WaitForSingleObject(mu, INFINITE);
        std::cout << "main  thread: " << i << std::endl;
        ReleaseMutex(mu);
    }
    WaitForSingleObject(ht1, INFINITE);
    CloseHandle(ht1);
    CloseHandle(mu);
    return 0;
}

输出

child thread: 0
main  thread: 0
child thread: 1
main  thread: 1
...
child thread: 18
main  thread: 18
child thread: 19
main  thread: 19

3 线程示例:

#include <iostream>
#include <windows.h>
static HANDLE mu;                       // handle: mutex
static HANDLE ht0;                      // handle: thread 0
static HANDLE ht1;                      // handle: thread 1
static DWORD  id0;                      // thread 0 id
static DWORD  id1;                      // thread 1 id
DWORD WINAPI Thread0(LPVOID) {
    for (int i = 0; i < 10; ++i) {
        WaitForSingleObject(mu, INFINITE);
        std::cout << "Thread0 : " << i << std::endl;
        ReleaseMutex(mu);
    }
    return 0;
}
DWORD WINAPI Thread1(LPVOID) {
    for (int i = 0; i < 10; ++i) {
        WaitForSingleObject(mu, INFINITE);
        std::cout << "Thread1 : " << i << std::endl;
        ReleaseMutex(mu);
    }
    return 0;
}
DWORD WINAPI Thread2(LPVOID) {
    for (int i = 0; i < 10; ++i) {
        WaitForSingleObject(mu, INFINITE);
        std::cout << "Thread2 : " << i << std::endl;
        ReleaseMutex(mu);
    }
    return 0;
}
int main()
{
    mu = CreateMutex(NULL,TRUE,NULL);   // main owns mutex
    ht0 = CreateThread(NULL, 0, Thread0, 0, 0, &id0);
    ht1 = CreateThread(NULL, 0, Thread1, 0, 0, &id1);
    Sleep(2);                           // let other threads get started
    ReleaseMutex(mu);                   // release mutex
    Thread2(0);
    WaitForSingleObject(ht0, INFINITE);
    WaitForSingleObject(ht1, INFINITE);
    CloseHandle(ht0);
    CloseHandle(ht1);
    CloseHandle(mu);
    return 0;
}

输出

Thread0 : 0
Thread1 : 0
Thread2 : 0
Thread0 : 1
Thread1 : 1
Thread2 : 1
...
Thread0 : 9
Thread1 : 9
Thread2 : 9

我知道互斥锁不负责公平份额的公共资源,但是:谁是?

实际计划由您的操作系统完成。

你还没有说这是什么,但通常不会在线程之间切换超过必要的频率,因为它效率低下(切换有一些成本)。

换句话说,你对"公平"的想法——大概是每个线程轮流进行的严格循环——将是一种昂贵的默认行为。无论如何,如果它是您想要的,您可以显式编码它。公平调度程序的通常目标涉及在可运行线程必须等待多长时间与在线程仍在执行(大概)有用工作时抢占线程的频率之间的某种权衡。

当然,操作系统行为还取决于您拥有的内核数量。你也没有提到这个。

。我如何在我的程序中实现它?

如果你在线程中做了一些合理的实际工作,你可能会发现你的调度程序的行为更符合你的喜好。这种人工测试很少给出有用的结果,特别是因为你在一个紧密的循环中执行少量的代码。