多线程。如何平等共享公共资源?

Multithreading. How to equally share common resources?

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

 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 }

returns 这种输出:

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 of child,cout of main,cout of child,cout of main 等等。我知道 mutex 不负责公平共享公共资源,但是:谁是?我如何将其实现到我的程序中?

谢谢。

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

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

没有帮助。

I know mutex is not responsible for fair share of common resources but then: who is ?

实际的调度是由您的操作系统完成的。

您还没有说这是什么,但通常不会在线程之间比必要更频繁地切换,因为它效率低下(切换会产生一些成本)。

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

OS 行为当然也取决于您拥有多少个核心。你也没有提到这个。

... And how do i implement that into my program?

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

原始代码在 windows 中有同样的问题,但我改用原生 windows 等价物,这个 windows 示例按照您期望的方式工作,在两个线程之间交替。 ReleaseMutex() 的每个实例都会导致 "other" 线程获取互斥锁和 运行。 main 中的 Sleep(2) 是确保 myFunc 首先开始循环的简单方法。

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

对于循环类型循环或线程之间的一般同步 and/or 进程,每个线程或进程使用一个信号量更好,因为任何线程或进程都可以递增(释放/发出信号)任何信号量。这个问题是信号量不是标准线程接口的本机部分,需要互斥锁和条件变量的某种组合来实现信号量的等价物。 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