zoukankan      html  css  js  c++  java
  • Linux学习5-线程

    线程

    1.1什么是线程?

      在一个程序中的多个执行路线就叫做线程(thread)。更准确的定义是:线程是一个进程内部的一个控制序列。

        要搞清楚fork系统调用和创建新线程之间的区别。当进程执行fork调用时,将创建出该进程的一份新的副本。这个新进程拥有自己的变量和自己的PID,它的时间调度也是独立的,它的执行(通常)

    几乎完全独立于父进程。当在进程中创建一个新线程时,新的执行线程将拥有自己的栈(因此也拥有自己的局部变量),但与它的创建者共享全局变量、文件描述符、信号处理函数和当前目录状态。  

    1.2第一个线程程序

      线程有一套完整的相关的函数库调用,它们绝大多数以pthread_开头。为了使用这些函数调用,我们必须定义宏_REENTRANT,在程序中包含头文件pthread.h,并且在编译程序时需要使用选项-lpthread来链接线程库。

    在一个多线程程序里,默认情况下,只有一个errno变量供所有的线程共享。在一个线程准备获取刚才的错误代码时,该变量很容易被另一个线程中的函数调用所改变。类似的问题还存在于fputs之类的函数中,这些函数通常用一个单独的全局性区域来缓存输出数据。

    为解决这个问题,需要使用可重入的例程。可重入代码可以被多次调用而仍然工作正常。编写的多线程程序,通过定义宏_REENTRANT来告诉编译器我们需要可重入功能,这个宏的定义必须出现于程序中的任何#include语句之前。

    _REENTRANT为我们做三件事情,并且做的非常优雅:

    (1)它会对部分函数重新定义它们的可安全重入的版本,这些函数名字一般不会发生改变,只是会在函数名后面添加_r字符串,如函数名gethostbyname变成gethostbyname_r。

    (2)stdio.h中原来以宏的形式实现的一些函数将变成可安全重入函数。

    (3)在error.h中定义的变量error现在将成为一个函数调用,它能够以一种安全的多线程方式来获取真正的errno的值。

    举个例子便于更好理解可重入的重要性:

    int g_val = 1;//定义一个全局变量
    
    void add()
    {
        g_val++;
    }

     如果有n个线程调用该函数,g_val是一个全局变量,如果加上-D _REENTRANT,则可重入,即g_val不会受其他线程的影响;但是没加-D _REENTRANT的时候,n-1个线程调用该函数时变量g_val就会彼此影响,出现不可预料的结果。

    不会影响的原因是,加-D _REENTRANT后虽然调用的还是这个函数,但是不同的是该机制自动把上面的函数变成了

    void add_r()
    {
        g_val++;
    }

    调用的函数不同了。

    创建新线程的函数:

    #include <pthread.h>

    int pthread_create(pthread_t  *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);

    第一个参数thread指向pthred_t类型数据的指针。线程被创建时,这个指针指向的变量中将被写入一个标识符,我们用该标识符来引用新线程。

    第二个参数用于设置线程的属性。一般不需要特殊的属性,所以可以设为NULL。

    第三个参数是一个函数地址,该函数以一个指向void的指针为参数,返回的也是一个指向void的指针。

    用fork调用后,父子进程将在同一个位置继续执行下去,只是fork调用的返回值上不同的;

    但对于新线程来说,我们必须明确地提供给它一个函数指针,新线程将在这个新位置开始执行。

    第四个参数是要传递给上面函数(第三个参数)的参数。

    该函数调用成功时返回值是0,如果失败则返回错误代码。

    终止线程的函数:

    #include <pthread.h>

    void ptherad_exit(void *retval);

    线程通过调用pthread_exit函数终止执行,就如同进程在结束时调用exit函数一样。该函数的作用是,终止调用它的线程并返回一个指向某个对象的指针。

    注意,绝对不能用它来返回一个指向局部变量的指针,因为线程在调用该函数后,这个局部变量就不再存在了,这将引起严重的程序漏洞。

    等待线程的函数:

    #include <pthread.h>

    int pthread_join(pthread_t th, void **thread_return);

    pthread_join函数在线程中的作用等价于进程中用来收集子进程信息的wait函数。

    第一个参数指定了要等待的线程,线程通过pthread_create返回的标识符来指定。

    第二个参数是一个指针,它指向了另一个指针,而后者指向了返回值。

    这个函数成功返回0失败返回错误代码。

    第一个线程示例代码:

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<string.h>
    #include<pthread.h>
    void *thread_function(void *arg);
    char message[]="hello world!";
    
    int main()
    {
        int res;
        pthread_t a_thread;
        void *thread_result;
       
        res = pthread_create(&a_thread,NULL,thread_function,(void *)&message);
        if(res != 0)
        {
            perror("create  thread is failed
     ");
            exit(EXIT_FAILURE);     
        }
        
        printf("Waiting for thread to finish...
    ");
        res = pthread_join(a_thread,&thread_result);
        if (res!=0)
        {
           perror("thread join failed!
    ");
           exit(EXIT_FAILURE);
          
        }  
    
        printf("Thread joined,it return %s
    ",(char *)thread_result);
        printf("message now is %s
    ",message);
        exit(EXIT_SUCCESS);   
     
    }
    
    void *thread_function(void *arg)
    {
        printf("th_func is running,Aragement was %s
    ",(char *)arg);
        sleep(5);
        strcpy(message,"bye");
        pthread_exit("Thanks for the CPU time
    "); 
    
    
    }

    执行结果:

     编译这个程序的时候,我们首先需要定义宏_REENTRANT。在少数系统上,可能还需要定义宏_POSIX_C_SOURCE。(然而我并没有加也能实现功能,待解决)

     接下来必须链接正确的线程库。 

     我系统默认的线程库是NPTL(查看头文件/usr/include/pthread.h 如果显示版权日期在2003年或更晚,那基本你的Linux发行版使用的是NPTL实现)

     

     使用如下命令编译和链接:

    cc -D_REENTRANT thread1.c -o thread1 -lpthread

    ps:我把cc 换成 gcc 编译通过。

     分析:main函数在创建新线程成功后,继续执行。然后执行pthread_join函数,一直等到它指定的线程终止才返回。然后主线程打印新线程返回值和全局变量后结束。

     而新创建的线程将会执行thread_function。先打印自己的参数,睡眠5S后,更改全局变量最后退出时向主线程返回一个字符串。

  • 相关阅读:
    JSON
    必须使用角色管理工具 安装或配置microsoft.net framework 3.5
    Backbone.js之view篇(三)
    MSDN Webcast 资源
    JS获取select 当前选种值
    我理解的js闭包
    javascript基础温习(一)
    js动态添加删除行
    delphi 版 sqlHelper第二版
    2012末日没有到来,继续我们的2013
  • 原文地址:https://www.cnblogs.com/xiaodeyao/p/6444009.html
Copyright © 2011-2022 走看看