c语言中的奇怪分段错误

c语言中的奇怪分段错误,c,gdb,pthreads,C,Gdb,Pthreads,这是密码 struct val { int x; int y; }; struct memory { struct val *sharedmem; }; struct val va; struct memory mem; void *myThreadFun(void *vargp) { va.x = 1; va.y = 2; mem.sharedmem = &va; sleep(1); printf("Printi

这是密码

  struct val {
  int x;
  int y;
  };

  struct memory {
  struct val *sharedmem;
  };

  struct val va;
  struct memory mem; 

  void *myThreadFun(void *vargp)
  {
  va.x = 1;
  va.y = 2;

  mem.sharedmem = &va;
  sleep(1);
  printf("Printing GeeksQuiz from Thread0 x = %d y = %d sharedmem = %d\n", va.x, va.y, mem.sharedmem->x);
  return NULL;
  }

  int main()
  {
   pthread_t tid;
   printf("Before Thread \n");
   asm volatile ("" ::: "eax", "esi");
   pthread_create(&tid, NULL, myThreadFun, NULL);

   asm volatile ("mfence" ::: "memory");
   printf("sharedmem = %d\n", mem.sharedmem->x); <----- **Here is the fault**
   pthread_join(tid, NULL); 
   printf("After Thread \n");
   exit(0);
  }

我尝试在被破坏的寄存器中添加几个通用寄存器,但仍然没有成功。在
gdb

下运行时,代码工作得非常好,以便在我的评论中进行一些扩展:

在编写单线程应用程序时,可以确定代码的早期部分将(有效地)始终在代码的后期部分之前执行

但是,当你有多个线程时,事情就会变得棘手。你怎么知道事情发生的顺序?线会很快开始吗?还是慢慢地?主线程会继续处理吗?或者操作系统会安排其他线程下一步运行,让“新”线程领先

在编写多线程应用程序时,2个(或更多)线程之间的这种“竞赛”是一个常见问题,它被称为“竞赛条件”。众所周知,很难追踪到这些类型的bug

如果你不采取任何措施来确保事件的顺序,你只是“希望”它们以正确的顺序发生。考虑到线程调度方式的自然变化,将会有一些回旋余地。除非您使用操作系统的一些同步功能来协调线程之间的关系

在本例中,我们正在查看的事件是设置
mem.sharedmem
变量。这是在
myThreadFun
线程中完成的。现在,
myThreadFun
线程能否在
main
线程尝试访问变量之前设置该变量?嗯,很难说清楚。可能是。可能不会

在调试器下运行这个会改变两个线程的运行速度吗?当然了。怎样很难预测

但是,如果在尝试访问变量之前将代码更改为加入线程,则可以确定线程已完成其工作,并且变量已准备好使用
join
是我提到的同步方法之一

查看您的代码,您可能认为
mfence
内存
缓存将为您执行这种类型的同步?对不起,不,没那么容易

学习如何在多个线程之间安全地同步工作是编程中一个非常棘手且具有挑战性的领域。重要、有用但富有挑战性


编辑1:

我还要指出的一点是,
lfence
sfence
mfence
都是相当低级的指令。人们(尤其是多线程新手)会发现,将(根据需要自动使用适当的汇编程序指令)与操作系统一起使用将使他们的生活变得更加轻松


关于原子学的教程和讨论比mfence多得多。它也更易于移植。

对于
myThreadFun
函数上的无效返回类型,您真的想返回
null
吗?@Boggartfly:函数返回
void*
,null是可以接受的,并且非常常用。嗯,请原谅我的无知,但如果它的返回类型是
void
,则它感觉完全没有必要。您尚未正式检查
pthread\u create()
pthread\u join()
中的返回值。这有什么区别吗?我不相信你的嵌入式汇编程序有什么不同。主线程启动并等待子线程完成。从线程的
printf()
调用中可以得到什么?在新创建的线程设置之前,没有任何东西可以阻止“主”线程访问
mem
。这几乎就是竞争条件的定义。实际上,我试图理解在多线程代码中共享变量的情况下,内存屏障是如何工作的。全屏障在屏障之前完成所有装载/存储操作。你能详细说明一下在这种情况下发生了什么吗?“你能详细说明一下在这种情况下发生了什么吗?”-嗯,现在发生的是主线程启动了线程,但是在新线程可以到达它设置值的点之前,主线程已经尝试读取它了。
mfence
屏障的目的不是试图阻止一个线程继续运行,直到另一个线程上发生了某些事情(这是一件好事。它如何知道哪个线程正在执行哪个存储?)。没有简单的解释来解释mfence,但也许能有所帮助?是否可以修改它以使其正常工作,或者我们必须使用锁?在加入后进行打印?可以通过删除所有asm、休眠和退出调用来“修复”原始代码(在那里做什么?)。您只需在printf之前移动
join
  mov eax,DWORD PTR [rax]