所讨论的计划是:
#include <pthread.h>
#include <stdio.h>
#define NUM_LOOPS 500000000
long long sum = 0;
/* Add or subtract the offset specified in arg from the sum NUM_LOOPS times */
void* counting_function(void* arg) {
int offset = *(int*)arg;
for (int i = 0; i < NUM_LOOPS; i++) {
sum += offset;
}
pthread_exit(NULL);
}
int main(void) {
// Spawn the threads
pthread_t id1;
int offset1 = 1;
pthread_create(&id1, NULL, counting_function, &offset1);
pthread_t id2;
int offset2 = -1;
pthread_create(&id2, NULL, counting_function, &offset2);
// Wait for threads to finish
pthread_join(id1, NULL);
pthread_join(id2, NULL);
printf("Sum = %lld\n", sum);
return 0;
}
我有一个全局变量long long sum=0
和一个用于NUM_LOOPS500000000
的宏,它指定要加起来的数字。
函数void*counting\u函数(void*arg)只是将偏移量添加到sumNUM\u循环次数。
如果我在没有线程的情况下运行它,但像这样一个接一个地调用函数
// sum is 0
counting_function(1);
// sum is now equal to 500000000
countnig_function(-1);
// sum is now equal to 0
我会得到正确答案0
但当使用线程时,我会得到一个非零的答案,每次都不同。
gcc -Wall -Wextra -Werror -std=c99 -pthread count.c && ./a.out
Sum = 40098157
./a.out
Sum = 303575386
在我看来,无论你从一个数字中加或减1的顺序如何,如果加和减的总数是相同的,那么答案应该总是0。
例如,假设NUM\u LOOPS=10。第一个线程在一行中加1三次,然后第二个线程在一行中减去1两次。现在总和等于1。然后第一个线程进行剩余的七次加法,因此总和等于8,然后第二个线程减去最后八次。总和为0。
你怎么可能得到一个不同于0的答案?答案不同于0意味着两个线程中的一个在NUM_LOOPS中增加或减少的数量超过了指定的数量。
这个问题可以通过在计数函数内部的临界部分
sum=偏移量
中添加互斥来解决,但我似乎无法弄清楚为什么没有互斥锁它就无法工作。
我错过了什么?
恭喜您尝试它!您已经发现尝试从两个线程更新相同的变量是有错误的。
您可以尝试一些解决方案。如果可能,您应该使用列表中较高的解决方案。
pthread
库将并行性硬塞进为单线程操作设计的语言中。我建议研究内存模型以及它们如何应用于多线程编程。
看看这里的线程和数据竞争部分。这里有一个与您的问题相关的示例。
这篇维基百科的文章有一个很好的章节介绍了种族状况。
有趣的是,这个问题早于pthread
。您可以在带有信号处理程序的“单线程”C程序中观察到相同的行为,其中主程序和信号处理程序都在更新相同的变量。
当线程添加到sum
时,它会分多个步骤执行:将sum
加载到寄存器中。向寄存器添加值。将寄存器中的值存储到sum
中。(这可能是两个步骤,例如在一个步骤中加载和添加,在另一个步骤中存储。)如果在此期间发生线程切换,您可以得到交错:线程1将sum
加载到寄存器中,例如值400,并将1添加到寄存器中,得到401。有一个开关,线程2将仍然值为400的sum
加载到寄存器中并添加−1,得到399。然后它存储它,所以sum
是399。后来,线程1存储401。
因此,减量到399丢失;它被401的延迟存储覆盖。
当然,在切换回线程1之前,线程2可能会运行一段时间,因此线程2可能会有相当多的减量,这些减量会被线程1的延迟存储擦除。
可能发生的是随机选择这种交织,有效地从一个或另一个线程中擦除各种数量的加法。其中许多会取消——加法的一些擦除会取消减法的一些擦除,并且多次运行的结果将显示以零为中心的总和分布(如果一个线程先于另一个线程启动会扭曲分布,则可能接近于零)。
这只是多线程可能出现的一个问题。特别是由于sum是long-long,因此它可以在一些目标体系结构中使用多个机器字来实现,因此必须分多个步骤加载和存储。然后,多线程可能导致一个线程存储新值的一部分,另一个线程进入并修改sum,第一个线程完成对另一部分的存储。这可能产生比仅仅缺少增量或减量更具灾难性的结果;它会更严重地破坏sum的值。为了避免这种情况,您需要使用原子对象。
如果一个表达式包含任何整数大小或更小的内容,其结果总是整数,即使两个字节之和适合一个字节。 为什么我们在一个字节中添加最后两个字节时会发生这种情况?没有编译器错误。
我试着运行一个程序,使用线程显示带有数字的乘法、除法、加法和减法表。 但是我希望数字被乘以或相加等。由用户选择。 也就是说,程序应该在用户为每个操作选择一个数字后运行,然后显示结果。
我是java新手,我创建了两个arraylists,然后在while循环中向用户请求一个数字,一旦我从第1点和第2点获得了所需数量的数字。我将arrayllists转换为数组。我需要比较两个点阵,然后将最低点的缺失数字设置为零。例如 点1=(12,123,123,435,6756,667)//6个数字 点 2=(23,13,35)//3 个数字 点 3=(23,13,35,0,0,0)//新数组替
我必须得到如下输出: 这是我的密码。没有错误。它以PlusThread开始并打印第一行。然后它将释放锁。之后,MultiplyThread开始运行。它将打印所有行,而不是通知PlusThread。 这是我的输出:
我有一些js使用readwrite事务对IndexedDB(在Chrome中)执行put,然后使用索引和readonly事务立即从相同的IndexedDB对象存储区查询。有时,我得到的结果不包括从我的put和其他时间他们的变化。IndexedDB中是否准备好了这样的脏内容?有办法避免吗? 可能是因为我使用了两个不同的TXN,而应该只使用一个(原因是这些调用实际上是api的一部分,该api将puts
我有一个完全使用Android Studio开发的应用程序。我需要创建第二个应用程序,这个我想用phonegapp来完成。这两个应用程序相互关联...所以理想情况下,我想将它们合并到同一个应用程序中...合并并不意味着它们需要一起工作。我希望它们一起下载(作为同一个应用程序),并且能够在应用程序中从一个下载到另一个...所以它们可以完全是两个独立的应用程序,但用户会认为它只是一个应用程序。我希望我