Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 为什么这个多线程程序可以工作(而不是崩溃)?_C_Multithreading_Pthreads - Fatal编程技术网

C 为什么这个多线程程序可以工作(而不是崩溃)?

C 为什么这个多线程程序可以工作(而不是崩溃)?,c,multithreading,pthreads,C,Multithreading,Pthreads,根据我的理解,如果两个或多个线程试图同时访问同一个内存块,至少应该是“抱怨” 我正在为一个类编写一个程序,该类计算回文(列表中前后显示的单词也算在内)。在我的多线程解决方案中,我生成了26个线程来处理字母表中的每个字母 int error=pthread\u create(&threads[i],NULL,computeAlindromes,args) “计算回文”仅在单词子列表中运行: void * computePalindromes(void * arguments) { stru

根据我的理解,如果两个或多个线程试图同时访问同一个内存块,至少应该是“抱怨”

我正在为一个类编写一个程序,该类计算回文(列表中前后显示的单词也算在内)。在我的多线程解决方案中,我生成了26个线程来处理字母表中的每个字母

int error=pthread\u create(&threads[i],NULL,computeAlindromes,args)

“计算回文”仅在单词子列表中运行:

void * computePalindromes(void * arguments) {
    struct arg_struct *args = (struct arg_struct *)arguments;
    int i;

    for (i = args->start; i < args->end; i++) {
        if (quickFind(getReverse(array[i]), 0, size - 1)) {
            printf("%s\n", array[i]);
        }
    }

    return NULL;
}
void*computeAlindromes(void*参数){
struct arg_struct*args=(struct arg_struct*)参数;
int i;
对于(i=args->start;iend;i++){
if(快速查找(getReverse(数组[i]),0,大小-1)){
printf(“%s\n”,数组[i]);
}
}
返回NULL;
}
现在,应该导致程序停止的段。我修改了quickSelect以在列表中查找相反的单词

int quickFind(char * string, int lower_bound, int upper_bound) {
    int index = ((upper_bound + lower_bound) / 2);
    //sem_wait(&semaphores[index]);
    if (upper_bound <= lower_bound) return (strcmp(string, array[index]) == 0);

    if (strcmp(string, array[index]) > 0) {
        //sem_post(&semaphores[index]);
        return quickFind(string, (index + 1), upper_bound);
    } else if (strcmp(string, array[index]) < 0) {
        //sem_post(&semaphores[index]);
        return quickFind(string, lower_bound, (index - 1)); 
    } else return 1;
}
int快速查找(字符*字符串,int下限,int上限){
int索引=((上界+下界)/2);
//sem_wait(&信号量[index]);
如果(上限为0){
//sem_post(和信号量[索引]);
返回快速查找(字符串,(索引+1),上限);
}else if(strcmp(字符串,数组[索引])小于0){
//sem_post(和信号量[索引]);
返回quickFind(字符串,下界,(索引-1));
}否则返回1;
}

你可以看到,我注释了一堆sem_post/waits

两个线程同时访问同一个内存没有问题,只要它们只读取内存而不写入内存。您对数据执行的任何操作实际上都不会修改它,因此线程并行执行所有这些操作是完全安全的


希望这有帮助

为了补充现有的优秀答案和评论,让我们将单核CPU中发生的事情可视化

现在很明显,在单核CPU中,一次只能运行一个线程/进程。操作系统调度程序实际上是另一个线程(一个非常特殊的线程…),它选择在接下来的15毫秒左右运行什么。但在任何一点上,任何正在运行的东西都只能访问内存。所以,虽然在练习中可能会感觉有很多东西同时跑,但实际上没有。只是有很多线程在很短的时间内一次运行一个线程

然而,我们现在都有多核CPU。这里发生的事情是,内核都能够寻址内存(这样或那样——像Netburst、QPI和Hypertransport这样的东西使事情变得复杂)。然而,在电子技术的深处,不可能有两个或多个内核同时访问同一个存储芯片。这样做会开始破坏一些东西,因此需要非常小心,以确保每次只有一个内核可以访问任何一块物理内存。因此,从根本上说,内存访问是在电子级串行化的

“啊哈”我听到你说,“那会让一切都变得很慢!”。你是对的,所以硬件人员通过缓存来解决这个问题,以帮助改善情况

因此,结果是,如果两个线程在不同的内核上尝试写入相同的地址,内存硬件会迫使它们轮流执行。如果访问真的是同时进行的,那么哪一个是第一个,这纯粹是运气。在软件中你不知道仲裁是如何进行的,所以你不能依赖它。在一台32位总线宽度的计算机上,您可以通过一次不间断的操作完成int32分配,但可能不是int64分配。但在64位总线宽度上,您可能会发现int64分配是不间断完成的

因此,如果在一个线程中有一条语句,比如a=b+c,在另一个线程中有a=10,并且它们在完全相同的时间运行(彼此之间的距离不到0.3纳秒,大致相当于3GHz的时钟频率),那么您不知道a是10,b+c,还是两者的混合(因为您不知道硬件将如何处理两个内核的写入,也不知道“a=”中实际涉及多少硬件内存事务,也不知道调度程序是否部分抢占了线程!)。无论发生什么情况,电子设备都不会抱怨(为什么要这样做?它所关心的只是不着火和爆炸,硬件的工作不是猜测软件想要做什么),所以操作系统和你的程序忘记了事情出错的事实

这就是为什么您需要信号量来序列化对“a”的访问,以便它至少是b+c或10,并且您需要使用其他程序流控件来确保这些控件中的正确一个是a中的结果

如果没有信号灯,你是在冒险(0.3纳秒非常短),在一台计算机上你可能永远看不到问题,但在另一台计算机上,你可能每次都会看到问题。你无法提前知道会发生什么。因此,为了可靠性,你必须正确编码

测试

用信号量控制的访问权限测试共享数据的程序既麻烦又困难。事实上,你不能仅仅通过测试来证明这样的程序是“完美的”。你所能做的就是评估程序设计的正确性,并检查源代码是否正确地实现了设计。这是一项艰巨的工作,而且是dif这需要程序员严格的纪律才能使它正确

还有其他的编程模式可以缓解这个问题。我一直在谈论通信顺序过程。这需要完全的思想转变才能进入其中,但回报是,测试程序更有可能揭示源代码中潜在的问题。从程序员的角度看,这是一种尝试ht