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

时间:2016-07-08 15:55:52

标签: c++ multithreading mutex

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

 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 of child,cout of main,cout of child,cout of main等等。我知道互斥体不对共同资源的公平份额负责,但那么:谁是谁?我如何在我的程序中实现它?

谢谢。

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

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

没有帮助。

2 个答案:

答案 0 :(得分:1)

  

我知道互斥体不对共同资源的公平份额负责,但随后是:谁是谁?

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

你还没有说明这是什么,但是通常在线程之间切换比必要更频繁,因为效率低(交换机有一些成本)。

换句话说,你对“公平”的想法 - 可能是每个线程轮流的严格循环 - 将是一种昂贵的默认行为。如果你想要它,你可以明确地编码它。公平调度程序的通常目标涉及在可运行线程必须等待多长时间之间进行某种权衡,以及在线程仍在进行(可能是)有用工作时预先占用线程的合理性。

当然,操作系统行为也取决于您拥有多少核心。你还没有提到这一点。

  

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

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

答案 1 :(得分:1)

原始代码在Windows中具有相同的问题,但我切换到使用本机窗口等效,并且此Windows示例按照您期望的方式工作,在两个线程之间交替。 ReleaseMutex()的每个实例都会导致“其他”线程获取互斥锁并运行。 main中的Sleep(2)是一种简单的方法,可以确保myFunc首先启动它的循环。

我还创建了一个带有main和两个线程的版本,共有三个线程。循环按顺序进行,因此看起来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