POSIX线程详解

因为自己刚学操作系统,第一次接触unix,对很多地方都是不懂得状态,所以这篇文章有任何错误的地方,欢迎批评指正。
这里我用两个例子来讲线程,POSIX线程中的thread1.c和thread2c

第一个线程

thread1.c

#include <pthread.h>

#include <stdlib.h>

#include <unistd.h>

 void *thread_function(void *arg) {

  int i;

  for ( i=0; i<20; i++) {

    printf("Thread says hi!\n");

    sleep(1);

  }

  return NULL;

}

int main(void) {

  pthread_t mythread;

   

  if ( pthread_create( &mythread, NULL, thread_function, NULL) ) {

    printf("error creating thread.");

    abort();

  }

  if ( pthread_join ( mythread, NULL ) ) {

    printf("error joining thread.");

    abort();

  }

  exit(0);

}

理解thread1.c

其实这是一个很简单的线程程序,虽然他没有什么实质上的有用的功能,但可以帮助理解线程的运行机制。
main() 中声明了变量 mythread,类型是 pthread_t。pthread_t 类型在 pthread.h 中定义,通常称为“线程 id”(缩写为 “tid”)。调用 pthread_create 函数创建一个真实活动的线程。pthread_create() 执行成功时返回零而失败时则返回非零值.
__pythread_create参数:
第一个参数为指向线程标识符的指针。
第二个参数用来设置线程属性。
第三个参数是线程运行函数的起始地址。
最后一个参数是运行函数的参数。
__

本例中,函数名为 thread_function()。当 thread_function() 返回时,新线程将终止。本例中,线程函数没有实现大的功能。它仅将 “Thread says hi!” 输出 20 次然后退出。注意 thread_function() 接受 void * 作为参数,同时返回值的类型也是 void *。这表明可以用 void * 向新线程传递任意类型的数据,新线程完成时也可返回任意类型的数据。那如何向线程传递一个任意参数?很简单。只要利用 pthread_create() 中的第四个参数。本例中,因为没有必要将任何数据传给微不足道的 thread_function(),所以将第四个参数设为 NULL。
若创建线程成功,其实这里已经有两个线程了,我们只创建了一个线程,但是这里还有一个主线程,这里还有一个函数,pthread_join(),
pthread_create() 将一个线程拆分为两个, pthread_join() 将两个线程合并为一个线程。pthread_join() 的第一个参数是 tid mythread。第二个参数是指向 void 指针的指针。如果 void 指针不为 NULL,pthread_join 将线程的 void * 返回值放置在指定的位置上。由于我们不必理会 thread_function() 的返回值,所以将其设为 NULL.

您会注意到 thread_function() 花了 20 秒才完成。在 thread_function() 结束很久之前,主线程就已经调用了 pthread_join()。如果发生这种情况,主线程将中断(转向睡眠)然后等待 thread_function() 完成。当 thread_function() 完成后, pthread_join() 将返回。这时程序又只有一个主线程。当程序退出时,所有新线程已经使用 pthread_join() 合并了。这就是应该如何处理在程序中创建的每个新线程的过程。如果没有合并一个新线程,则它仍然对系统的最大线程数限制不利。这意味着如果未对线程做正确的清理,最终会导致 pthread_create() 调用失败。

第二段代码

thread2.c

#include <pthread.h>

#include <stdlib.h>

#include <unistd.h>

#include <stdio.h>

int myglobal;

 void *thread_function(void *arg) {

  int i,j;

  for ( i=0; i<20; i++) {

    j=myglobal;

    j=j+1;

    printf(".");

    fflush(stdout);

    sleep(1);

    myglobal=j;

  }

  return NULL;

}

int main(void) {

  pthread_t mythread;

  int i;

  if ( pthread_create( &mythread, NULL, thread_function, NULL) ) {

    printf("error creating thread.");

    abort();

  }

  for ( i=0; i<20; i++) {

    myglobal=myglobal+1;

    printf("o");

    fflush(stdout);

    sleep(1);

  }

  if ( pthread_join ( mythread, NULL ) ) {

    printf("error joining thread.");

    abort();

  }

  printf("\nmyglobal equals %d\n",myglobal);

  exit(0);

}

理解thread2.c

照第一段程序的分析方法,这段程序新建了一个线程,主线程和新线程都将全局变量加一20次,但是结果往往出乎意料。
输出结果是这样子的:

$ ./thread2

..o.o.o.o.oo.o.o.o.o.o.o.o.o.o..o.o.o.o.o

myglobal equals 21


很奇怪,为什么会这样子呢?
因为在thread_function函数中调用了局部变量,在sleep之后才将j赋值给全局变量myglobal,那么在sleep这段时间内,主线程对myglobal的操作就被myglobal=j;给覆盖了。所以得到的结果并不是所期望的,如果要解决这样的一个问题,就是避免在新建的线程中使用局部变量。

最近在学操作系统,在慢慢接触操作系统的过程中,发现操作系统真的没有自己想的那么简单,我看知乎上很多大佬都说想要学好操作系统,一定要自己写一个操作系统才行,一个学期的学习都只是学习理论,学的都是皮毛。我现在想来的确如此,想要学精学透太难了。

参考资料:https://www.ibm.com/developerworks/cn/linux/thread/posix_thread1/index.html

猜你喜欢

转载自blog.csdn.net/qq_41040989/article/details/89737872
今日推荐