【问题标题】:Confusion with thread synchronization [pthreads]与线程同步混淆 [pthreads]
【发布时间】:2017-05-25 00:40:59
【问题描述】:

我有一个问题线程很长一段时间。这段代码应该有一个工作线程在主线程打印出共享整数时递增共享整数的值。但是,我没有得到预期的输出。

#include <pthread.h>
#include <unistd.h>
#include <stdio.h>

pthread_mutex_t lock;
int shared_data = 0; //shared data


// Often shared data is more complex than just an int.
void* thread_function(void* arg)
{
    int i;
    for (i = 0; i < 10; ++i)
    {
        // Access the shared data here.
        pthread_mutex_lock(&lock);
        shared_data++;
        pthread_mutex_unlock(&lock);
    }

    return NULL;
}

int main(void)
{
    pthread_t thread;
    int i;
    void* exit_status;
    // Initialize the mutex before trying to use it.
    pthread_mutex_init(&lock, NULL);
    pthread_create(&thread, NULL, thread_function, NULL);
    // Try to use the shared data.
    for (i = 0; i < 10; ++i)
    {
        sleep(1);
        pthread_mutex_lock(&lock);
        printf ("\r for i= %d Shared integer 's value = %d\n", i, shared_data);
        pthread_mutex_unlock(&lock);
    }
    printf("\n");
    pthread_join(thread, &exit_status);
    // Clean up the mutex when we are finished with it.
    pthread_mutex_destroy(&lock);

    return 0;
}

这是我的期望:

for i=0 Shared Integer 's value = 0
for i=1 Shared Integer 's value = 1 
for i=3 Shared Integer 's value = 2
...
for i=10 Shared Integer 's value =10

但结果是:

for i=0 Shared Integer 's value = 0
for i=1 Shared Integer 's value = 10
for i=3 Shared Integer 's value = 10
...
for i=10 Shared Integer 's value =10

那我该如何解决呢?

【问题讨论】:

  • 请使用一致且适当的缩进格式化您的代码以使其可读。
  • 然后向我们解释您认为程序的哪一部分会按照您描述的方式对输出进行排序。您似乎假设主线程将获得锁,然后是子线程,然后是主线程,然后是子线程,等等。没有这样的保证。
  • 第一次主迭代的连接等待线程完成,因此在第二次迭代中你得到 10 的最终结果。
  • @NikolaiFetissov 谢谢,我已经编辑了我的代码格式,实际上我期待线程增加共享数据的值,然后主循环中的循环打印值,从0 到 10 ,这里不是这样
  • 为什么在需要两个信号量的情况下,开发人员总是滥用互斥锁?

标签: c pthreads mutex rtos qnx


【解决方案1】:

主线程和您的工作线程同时运行。也就是说,如果没有额外的同步,让这些 for 循环完全一致几乎是不可能的。

您的输出正是您所期望的。产生线程所花费的时间允许主线程在另一个线程更改共享数据之前进行打印。然后,打印需要很长时间,以至于另一个线程完全完成其循环并将共享数据增加到 10,然后主线程才能进行第二次迭代。

在一个完美的世界里,这个使用条件变量的小技巧会让你得到你想要的: 编辑:条件变量对此是个坏主意。这是使用伪原子变量的工作版本,包含 UB :)

#include <pthread.h>
#include <unistd.h>
#include <stdio.h>

pthread_mutex_t want_incr_mut;
pthread_mutex_t done_incr_mut;
int want_incr = 0;
int done_incr = 0;
int shared_data = 0; //shared data

// Not using atomics, so...

void wait_for_want_increment()
{
    while (1)
    {
        pthread_mutex_lock(&want_incr_mut);
        if (want_incr)
        {
            pthread_mutex_unlock(&want_incr_mut);
            return;
        }
        pthread_mutex_unlock(&want_incr_mut);
    }
}

void wait_for_done_incrementing()
{
    while (1)
    {
        pthread_mutex_lock(&done_incr_mut);
        if (done_incr)
        {
            pthread_mutex_unlock(&done_incr_mut);
            return;
        }
        pthread_mutex_unlock(&done_incr_mut);
    }
}


void done_incrementing()
{
    pthread_mutex_lock(&done_incr_mut);

    done_incr = 1;
    pthread_mutex_lock(&want_incr_mut);
    want_incr = 0;
    pthread_mutex_unlock(&want_incr_mut);

    pthread_mutex_unlock(&done_incr_mut);
}

void want_increment()
{
    pthread_mutex_lock(&want_incr_mut);

    want_incr = 1;
    pthread_mutex_lock(&done_incr_mut);
    done_incr = 0;
    pthread_mutex_unlock(&done_incr_mut);

    pthread_mutex_unlock(&want_incr_mut);
}

// Often shared data is more complex than just an int.
void* thread_function(void* arg)
{
    int i;
    for (i = 0; i < 10; ++i)
    {
        wait_for_want_increment();
        // Access the shared data here.
        shared_data++;
        done_incrementing();
    }

    return NULL;
}

int main(void)
{
    pthread_t thread;
    int i;
    void* exit_status;

    // Initialize the mutex before trying to use it.
    pthread_mutex_init(&want_incr_mut, NULL);
    pthread_mutex_init(&done_incr_mut, NULL);
    pthread_create(&thread, NULL, thread_function, NULL);

    // Try to use the shared data.
    for (i = 0; i <= 10; ++i)
    {
        printf("\r for i= %d Shared integer 's value = %d\n", i, shared_data);
        if (i == 10) break;

        want_increment();
        wait_for_done_incrementing();
    }
    printf("\n");
    pthread_join(thread, &exit_status);
    // Clean up the mutexes when we are finished with them.
    pthread_mutex_destroy(&want_incr_mut);
    pthread_mutex_destroy(&done_incr_mut);

    return 0;
}

在这里,我们只是告诉工人我们想要一个增量,并等待他说他已经完成,然后再继续。同时,worker 等待我们想要一个增量,并在他完成时告诉我们。

我还将主循环更改为 10,因为我认为这是你想要的。

这是我的输出:

for i= 0 Shared integer 's value = 0
for i= 1 Shared integer 's value = 1
for i= 2 Shared integer 's value = 2
for i= 3 Shared integer 's value = 3
for i= 4 Shared integer 's value = 4
for i= 5 Shared integer 's value = 5
for i= 6 Shared integer 's value = 6
for i= 7 Shared integer 's value = 7
for i= 8 Shared integer 's value = 8
for i= 9 Shared integer 's value = 9
for i= 10 Shared integer 's value = 10

【讨论】:

  • 先谢谢你,但我该如何解决这个问题?
  • 老实说,您不想这样做...但是,您可以使用条件变量。您希望主线程在打印之前等待工作线程完成每个增量,并且您希望工作线程在主线程发出完成打印的信号后继续。
  • 正是我想要的,这似乎有点困难。 .
  • 谢谢,但我仍然有同样的问题,它现在显示我为 i=0 共享整数的值 =0 为 i=1 共享整数的值=11 ,为 i=2 共享整数的值= 11 ... 直到 i=10
  • @azert123 嗯。我使用 MinGW 在 Windows 上对其进行了测试。也许我的代码中有一个恰好在这里工作的错误?您是否尝试完全复制我的代码?我听起来你只是将循环更改为 10...
猜你喜欢
  • 2021-03-17
  • 2011-01-06
  • 2017-09-14
  • 1970-01-01
  • 2020-01-09
  • 1970-01-01
  • 1970-01-01
  • 2023-01-22
  • 2023-03-17
相关资源
最近更新 更多