我对线程的概念很陌生。我在C中处理生产者消费者问题,但是当与producer并行时,使用者线程不会运行。
我的代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
int S;
int E;
int F;
void waitS(){
//printf("hbasd");
while(S<=0);
S--;
}
void signalS(){
S++;
}
void waitE(){
while(E<=0);
E--;
}
void signalE(){
E++;
}
void waitF(){
while(F<=0);
F--;
}
void signalF(){
F++;
}
int p,c;
void* producer(void *n){
int *j = (int *)n;
int i = *j;
while(1){
waitS();
waitE();
printf("Producer %d\n",E);
signalS();
signalF();
p++;
if(p>=i){
printf("Exiting: producer\n");
pthread_exit(0);
}
}
}
void* consumer(void *n){
int *j = (int *)n;
int i = *j;
while(1){
waitS();
waitF();
printf("Consumer %d\n",E);
signalS();
signalE();
c++;
if(c>=i){
printf("Exiting Consumer\n");
pthread_exit(0);
}
}
}
int main(int argc, char* argv[]){
int n = atoi(argv[1]);
E = n;
S = 1;
F = 0;
int pro = atoi(argv[2]);
int con = atoi(argv[3]);
pthread_t pid, cid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&pid,&attr,producer,(void *)&pro);
pthread_create(&cid,&attr,consumer,(void *)&con);
pthread_join(pid,NULL);
pthread_join(cid,NULL);
}当输入为./a.out 3 4 3,即n=3,pro = 4,con =3时
我没办法出去,只是一种死锁的情况。
我希望输出像
生产者2生产者1生产者0消费者0消费者1生产者0退出:生产者消费者0退出:消费者
...similar输出,其中生产者运行4次,消费三次
当我给出像./a.out 4 4 3这样的输入时,我得到以下输出
生产者3生产者2生产者1生产者0退出:生产者消费者0消费者1消费者退出:消费者
从结果中,我得到了一个结论,即线程生产者正在执行第一,然后是线程使用者。
我希望它们同时执行,这样当给出像3,4,3这样的测试用例时,我得到一个类似于第一个预期输出的答案。
发布于 2018-02-05 10:07:47
通常,您应该使用同步原语,但与其他答案不同,我认为,如果我们在x86体系结构和上运行这个程序,可以防止编译器优化代码中的一些关键部分,那么我们可能不需要任何答案。
根据维基百科,x86体系结构几乎具有顺序一致性,这足以实现生产者-消费者算法。
成功实现这种生产者-消费者算法的规则非常简单:
X,另一个线程只从X读取。volatile关键字。下面是基于您的代码的工作示例。生产者生产的数字从5降到0,消费者消耗它们。请记住,由于其他体系结构的排序较弱,这只适用于x86:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
volatile int P = 0;
volatile int C = 0;
volatile int value = 0;
void produce(int v)
{
value = v;
P++;
}
int consume()
{
int v = value;
C++;
return v;
}
void waitForConsumer()
{
while (C != P)
;
}
void waitForProducer()
{
while (C == P)
;
}
void *producer(void *n)
{
int i = *(int *)n;
while (1) {
waitForConsumer();
printf("Producing %d\n", i);
produce(i);
i--;
if (i < 0) {
printf("Exiting: producer\n");
pthread_exit(0);
}
}
}
void *consumer(void *n)
{
while (1) {
waitForProducer();
int v = consume();
printf("Consumed %d\n", v);
if (v == 0) {
printf("Exiting: consumer\n");
pthread_exit(0);
}
}
}
int main(int argc, char *argv[])
{
int pro = 5;
pthread_t pid, cid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&pid, &attr, producer, (void *)&pro);
pthread_create(&cid, &attr, consumer, NULL);
pthread_join(pid, NULL);
pthread_join(cid, NULL);
}产生以下结果:
$ ./a.out
Producing 5
Producing 4
Consumed 5
Consumed 4
Producing 3
Producing 2
Consumed 3
Consumed 2
Producing 1
Producing 0
Exiting: producer
Consumed 1
Consumed 0
Exiting: consumer要了解更多信息,我推荐Herb的演示文稿“atomic<> Weapons”,它很长,但是您需要知道关于排序和atomics的所有信息。
尽管上面列出的代码在x86上可以正常工作,但我确实鼓励您查看上面的演示文稿,并使用内置的atomics,比如__atomic_load_n(),它将在任何平台上生成正确的组装代码。
发布于 2018-02-01 06:22:48
您正在访问来自不同线程的非原子变量,而没有任何类型的同步;这是一种争用条件,它会导致未定义的行为。
特别是,现代CPU为每个CPU核心提供了独立的寄存器和单独的缓存,这意味着如果运行在CPU核心#1上的线程修改了变量的值,这种修改可能会在CPU #1的缓存中保留很长一段时间,而不会被“推送”到RAM,因此在CPU核心#2上运行的另一个线程可能在很长一段时间内不会“看到”线程#1的更新(或者永远不会)。
处理此问题的传统方法是使用一个或多个互斥变量(请参阅pthread_mutex_init()、pthread_mutex_lock()、pthread_mutex_unlock()等)序列化对共享变量的访问,或者使用原子变量而不是标准ints来访问要同时从多个线程访问的值。这两种机制都有保护措施,以确保不发生未定义的行为(如果使用正确的话)。
发布于 2018-02-01 08:27:15
在没有同步的情况下,不能从两个不同的线程访问相同的内存。线程的标准清楚地说明了它的这里。
应用程序应确保由多个控制线程(线程或进程)对任何内存位置的访问受到限制,以便控制线程不能读取或修改内存位置,而另一个控制线程可能正在修改该位置。这种访问受到限制,使用同步线程执行的函数,并与其他线程同步内存。
此外,即使我们忽略了许多CPU不同步内存,除非您明确要求它们同步,但在普通C中,您的代码仍然是不正确的,因为如果可以在后台更改变量,它们应该是不稳定的。但是,即使在某些CPU上易失性可能会有所帮助,但对于p线程来说,这是不正确的。
只要使用适当的锁,不要旋转全局变量,有方法来加热比使用CPU便宜得多的房间。
https://stackoverflow.com/questions/48556171
复制相似问题