当前位置: 首页 > 知识库问答 >
问题:

c - 这段多线程代码应该怎么理解?

公良琛
2024-01-22
#include <stdio.h>#include <pthread.h>long count = 0;void *run(void *n) {  long i, nums = *((long *)n);  for (i = 0; i < nums; i++) {    count++;  }  return NULL;}void main() {  pthread_t t1, t2;  long nums = 100000;  pthread_create(&t1, NULL, run, &nums);  pthread_create(&t2, NULL, run, &nums);  pthread_join(t1, NULL);  pthread_join(t2, NULL);  printf("count = %d", count);}

如果我把 nums 设置为 10000,那么每次都准确输出 20000。或者设置的值比 10000 要少时也能准确输出。如果我把 nums 设置为 100000,那么每次输出的值都好像是随机的,范围在 100000 到 200000 之间。

如果是因为线程之间执行顺序的问题导致输出不确定,那为什么当值为 10000 时输出的值是比较确定的?

共有4个答案

饶骁
2024-01-22

设置为10000或者更少的时候,执行时间短,另一个线程还没来得及和当前执行的线程竞争资源,当前执行的线程就执行完了。

乜清野
2024-01-22

个人理解和CPU调度有关

两个线程全局共享变量count;

  1. 当nums <= 10000 时候,调度器可能把两次任务串行的放到同一个执行单元执行(线程Or?),不会产生数据竞争的情况,现在cpu的频率都是Ghz运行速度是很快的,是有这种可能的
  2. 当nums > 10000 时候,调度器可能把两次任务串行的放到两个不同执行单元执行(线程Or?),基本就会产生数据竞争的情况,比如某一次调度过程中此时缓存中的count = 1000,调度器切换到p1执行,cpu读取count的值然后进行加法运算后count的是1001,接下来p1准备把这个值(1001)写会到缓存中时,调度器切换到p2执行了, 然后cpu再读取缓存中的值1000,然后进行加法运算准备把这次运算后的结果(1001)写回到缓存时,调度器又切换到了p1,然后p1把它的运算结果1001写会缓存,然后调度器这个时候又切换到了p2,然后p2又把它的运算结果1001也写会到了缓存此时尽管两个线程进行了2次加法运行但是结果却增加了一次 这就是值小于等于 20000的原因

有一本书写的特别清楚我给你找找:

操作系统导论
这本数的第2章介绍并发的时候有详细介绍

另外一本是:
Is Parallel Programming Hard, And, If So, What Can You Do About It? (Release v2023.06.11a)

虞博涛
2024-01-22

这种情况下,两个线程有对同一变量的未经同步的写操作,所以存在 data race 。

存在 data race 的情况下,语言并不保证程序的结果。

也许你换台机器、换个编译器、换个编译选项、等等,就又能看到不同的现象。


对于貌似“确定”的结果,可能是由于在一个线程启动后,另一个线程还没有开始运行的很短的时间的,第一个线程的 10000 个循环已经做完了。所以两个线程对同一变量的操作实际并没有“重叠”。

当然也还可能有其它原因。

金阳曜
2024-01-22

这段代码是一个简单的多线程程序,用于展示线程并发和增加全局变量的复杂行为。首先,我们先对代码进行简单解释。

#include <stdio.h>#include <pthread.h> 是标准库的头文件,分别用于标准输入输出和 POSIX 线程操作。

long count = 0; 定义了一个全局的 long 类型变量 count 并初始化为 0。

void *run(void *n) 是一个线程函数,它接受一个 void 指针 n,然后将其转换为 long 指针,解引用得到 nums 的值,然后在循环中将 count 增加 nums 次。

void main() 是程序的主函数,其中:

  • pthread_t t1, t2; 定义了两个线程的句柄。
  • long nums = 100000; 定义并初始化了一个 long 类型的变量 nums,值为 100000。
  • pthread_create(&t1, NULL, run, &nums);pthread_create(&t2, NULL, run, &nums); 创建了两个线程,并启动它们运行 run 函数。
  • pthread_join(t1, NULL);pthread_join(t2, NULL); 是阻塞调用,等待线程 t1 和 t2 完成。
  • 最后,程序输出 count 的值。

现在,让我们来看看程序的输出问题。当 nums 为 10000 时,每次都能准确输出 20000,这是因为两个线程每次都会将 count 增加 10000 次,所以总和是 20000。当 nums 的值比 10000 小时,结果也是准确的,因为两个线程增加的次数是固定的。

然而,当 nums 为 100000 时,输出是随机的。这是因为线程的调度是不确定的,可能存在一个线程先于另一个线程开始或结束的情况。这导致两个线程可能不会同时完成,因此输出的 count 值会在 100000 到 200000 之间波动。

此外,由于 count 是全局变量,没有进行同步操作(如互斥锁),所以当两个线程同时修改它时,可能会出现数据竞争(data race)。这可能是导致输出不稳定的原因之一。为了解决这个问题,可以使用互斥锁(如 pthread_mutex_t)来保护对 count 的访问。

 类似资料:
  • 执行者。newFixedThreadPool(5)在池中创建5个线程,然后在循环中再创建100个线程。这种理解正确吗?然后池中的5个线程将执行100个工作线程队列中的每个线程。 总共创建了105个线程?我原以为只创建了5个线程,但每个也是一个线程。

  • 大概功能就是计算一个数组内的元素的和,怎么约束 T 让这段程序编译通过

  • 这是怎么回事?我不明白count()是如何既等于withCallback又有一个主体的;不知何故,它是在withCallback返回的dataframe上调用的,但我不明白语法。

  • 实际上,下面的代码不能用这个命令用Clang编译: . 我只想模仿与C中的“交换习惯用法”相同的行为,使用“using directive”来启用ADL。但是下面的代码哪里错了呢?预期的调用优先级应为:N1::foo 错误消息: 更新: 我将N2::foo更改为可以在某种程度上模仿std::交换的模板方法。所以,这里的问题是为什么不能由在函数中?因为当它们具有相同的优先级时,该函数应该比要调用的模