【问题标题】:Unable to print even and odd numbers asynchronous无法异步打印偶数和奇数
【发布时间】:2017-09-02 19:36:31
【问题描述】:

我想在主线程中打印奇数,在新线程中打印偶数。我尝试编写一个程序,但它只打印奇数而不是偶数。我试图寻找线索以找出问题所在,但没有找到任何线索。

这是我的代码。

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#define MAX 1000

int count = 0;

void print_odd_numbers();
void *print_even_numbers();

int main() {
    pthread_t t;
    int iret;
    iret = pthread_create(&t, NULL, print_even_numbers, NULL);
    print_odd_numbers();        
    pthread_join(t, NULL);
    return 0;
}

void print_odd_numbers() {
    while(count <= MAX) {
        if(count % 2 == 1) {
            printf("%d\n", count);
        }
        count++;
    }
}

void *print_even_numbers() {
    while(count <= MAX) {
        if(count % 2 == 0) {
            printf("%d\n", count);
        }
        count++;
    }
    pthread_exit(NULL);
}

【问题讨论】:

  • 每个线程都需要自己的 'count' 变量。不要使用全局变量!
  • @MartinJames 我见过许多程序,它们将计数保持在全局范围内。实际上这两个线程应该并行运行,所以它们引用的计数应该相同。据我所知。如果我错了,请帮忙。
  • @Priyanka Naik 不,这是不正确的。原因与 CPU 缓存一致性有关。除非您使用旨在以缓存一致方式原子地增加共享值的特定操作,否则您可以有两个 CPU(或内核),每个 CPU(或内核)都在缓存中迭代它们自己的变量副本,然后它们都将它们的副本写回一点。此外,编译器不必从内存中重新读取值,但这是另一回事。
  • @PriyankaNaik '我见过许多程序,它们将计数保持在全局范围内。' - 不幸的是,考虑到 umm.. 可能的“线程简介”网站/书籍的“不那么出色”的质量,我并不感到惊讶:(
  • @Martin James 哈哈!!

标签: c multithreading pthreads


【解决方案1】:

print_odd_numbersprint_even_numbers 都会增加 count,即使它们不打印任何内容,这也是问题的根源。

count 的所有增量都将在print_odd_numbers 中进行,count 将在print_even_numbers 开始之前设置为MAX

count 设置为函数的本地可能会有所帮助,或者在 count 的递增方式上更加小心是另一种方式 - 如果您保留其全局性质,那么您应该考虑使用 原子增量 em>。

【讨论】:

  • 据我所知,线程应该共享一个公共地址空间。所以两个线程都应该有一个共同的变量计数。此外,这些进程应该并行运行。
  • 或者使用互斥锁保护对线程间共享资源(count)的访问。
【解决方案2】:

如果您不让编译器知道您的计数被多个线程使用,那么它可以完全重新排列您的代码。例如,编译器可能将print_odd_numbers 更改为更像

void print_odd_numbers() {
    if (count <= MAX) {
        if(count % 2 == 1) {
            printf("%d\n", count);
        }
        count++;
    }
    while(count <= MAX) {
        printf("%d\n", count);
        count += 2;
    }
}

此外,每个 CPU 或内核都将使用它自己的 count 副本,因为我们拥有所有这些出色的 CPU 缓存,可以大大加快速度。除非您以某种方式让编译器知道该内存位置已被其他线程使用,否则每个线程(如果在其自己的内核上运行)将只使用自己的副本,然后在某个时候将该值写回主内存。

您需要使用原子增量指令或使用互斥锁、临界区、信号量等保护全局。当您使用信号量、互斥量等时,编译器会自动生成 LOCK 指令(在 x86 或类似的其他拱门),这迫使 CPU 以跨 CPU 和内核的连贯方式管理其缓存中的内存。

pthreads: If I increment a global from two different threads, can there be sync issues?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-08
    相关资源
    最近更新 更多