`
forfuture1978
  • 浏览: 412952 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

高级Linux程序设计第四章:线程

阅读更多

  • 要想使用POSIX标准线程API(pthreads),需要连接libpthread.so库到程序中。

1、创建线程

  • 进程中的每个线程都有一个线程号,类型为pthread_t。

  • 用pthread_self函数可以返回当前线程的线程号。

  • 线程号之间的比较可以用函数pthread_equal。

if (!pthread_equal (pthread_self (), other_thread))

    pthread_join (other_thread, NULL);

  • 每个线程执行一个线程函数:

void * function(void *)

  • 用函数pthread_create函数可以创建一个线程:

    • 第一个参数是一个pthread_t类型的变量的指针,用于存储线程号。

    • 第二个参数是一个pthread_attr_t类型的线程属性对象,NULL表示使用默认属性

    • 第三个参数是一个指向线程函数的指针

    • 第四个参数是void指针,指向传给线程的参数

  • 编译并链接程序:

 

% cc -o thread-create thread-create.c –lpthread

  • 线程退出有两种方式:

    • 线程函数退出。线程函数的返回值就是线程的返回值。

    • 显式调用pthread_exit,其参数为线程的返回值。

#include <pthread.h>

#include <stdio.h>

/* Prints x’s to stderr. The parameter is unused. Does not return. */

void* print_xs (void* unused)

{

    while (1)

        fputc (‘x’, stderr);

    return NULL;

}

/* The main program. */

int main ()

{

    pthread_t thread_id;

    /* Create a new thread. The new thread will run the print_xs function. */

    pthread_create (&thread_id, NULL, &print_xs, NULL);

    /* Print o’s continuously to stderr. */

    while (1)

        fputc (‘o’, stderr);

    return 0;

}

1.1、向线程传递数据

Listing 4.2 (thread-create2) Create Two Threads

#include <pthread.h>

#include <stdio.h>

/* Parameters to print_function. */

struct char_print_parms

{

    /* The character to print. */

    char character;

    /* The number of times to print it. */

    int count;

};

/* Prints a number of characters to stderr, as given by PARAMETERS,

which is a pointer to a struct char_print_parms. */

void* char_print (void* parameters)

{

    /* Cast the cookie pointer to the right type. */

    struct char_print_parms* p = (struct char_print_parms*) parameters;

    int i;

    for (i = 0; i < p->count; ++i)

        fputc (p->character, stderr);

    return NULL;

}

/* The main program. */

int main ()

{

    pthread_t thread1_id;

    pthread_t thread2_id;

    struct char_print_parms thread1_args;

    struct char_print_parms thread2_args;

    /* Create a new thread to print 30,000 ’x’s. */

    thread1_args.character = ’x’;

    thread1_args.count = 30000;

    pthread_create (&thread1_id, NULL, &char_print, &thread1_args);

    /* Create a new thread to print 20,000 o’s. */

    thread2_args.character = ’o’;

    thread2_args.count = 20000;

    pthread_create (&thread2_id, NULL, &char_print, &thread2_args);

    return 0;

}

此程序有一个Bug,主线程创建线程的时候,穿进去的参数是局部变量,如果主线程在从线程之前退出,则局部变量会被释放,然而从线程却可能仍然要访问这些变量,就会出错。因而应该应用join使得主线程等待从线程结束。

1.2、Join线程

  • phread_join有两个参数:

    • 第一个参数是要join的线程号。

    • 第二个参数是一个void指针,指向一个变量来接收线程的返回值。

 

Revised Main Function for thread-create2.c

int main ()

{

    pthread_t thread1_id;

    pthread_t thread2_id;

    struct char_print_parms thread1_args;

    struct char_print_parms thread2_args;

    /* Create a new thread to print 30,000 x’s. */

    thread1_args.character = ’x’;

    thread1_args.count = 30000;

    pthread_create (&thread1_id, NULL, &char_print, &thread1_args);

    /* Create a new thread to print 20,000 o’s. */

    thread2_args.character = ’o’;

    thread2_args.count = 20000;

    pthread_create (&thread2_id, NULL, &char_print, &thread2_args);

    /* Make sure the first thread has finished. */

    pthread_join (thread1_id, NULL);

    /* Make sure the second thread has finished. */

    pthread_join (thread2_id, NULL);

    /* Now we can safely return. */

    return 0;

}

1.3、线程返回值

用线程计算素数

#include <pthread.h>

#include <stdio.h>

/* Compute successive prime numbers (very inefficiently). Return the

Nth prime number, where N is the value pointed to by *ARG. */

void* compute_prime (void* arg)

{

    int candidate = 2;

    int n = *((int*) arg);

    while (1) {

        int factor;

        int is_prime = 1;

        /* Test primality by successive division. */

        for (factor = 2; factor < candidate; ++factor)

            if (candidate % factor == 0) {

                is_prime = 0;

                break;

            }

        /* Is this the prime number we’re looking for? */

        if (is_prime) {

            if (--n == 0)

            /* Return the desired prime number as the thread return value. */

                return (void*) candidate;

        }

        ++candidate;

    }

    return NULL;

}

int main ()

{

    pthread_t thread;

    int which_prime = 5000;

    int prime;

    /* Start the computing thread, up to the 5,000th prime number. */

    pthread_create (&thread, NULL, &compute_prime, &which_prime);

    /* Do some other work here... */

    /* Wait for the prime number thread to complete, and get the result. */

    pthread_join (thread, (void*) &prime);

    /* Print the largest prime it computed. */

    printf(“The %dth prime number is %d.\n”, which_prime, prime);

    return 0;

1.4、线程属性

  • 使用线程属性:

    • 创建pthread_attr_t对象。

    • 调用pthread_attr_init来初始化为默认属性。

    • 可以修改线程属性对象,设置需要的属性值。

    • 调用pthread_create的时候将属性对象指针传入。

    • 调用pthread_attr_destroy来释放属性对象。

    • pthread_attr_t本身不能自动回收,但是可以用pthread_attr_init函数重新初始化线程对象。

  • 使用detach state属性

    • 一个线程可以默认状态下被创建称为可join的线程,也可以创建为detached线程。

    • 一个可join的线程在结束后不会自动被回收,而是要其他线程调用pthread_join来获得此线程的返回值。

    • 一个detached线程在结束后会自动被回收。

    • 用函数pthread_attr_setdetachstate来设定线程属性对象的detach state.

 

#include <pthread.h>

void* thread_function (void* thread_arg)

{

/* Do work here... */

}

int main ()

{

    pthread_attr_t attr;

    pthread_t thread;

    pthread_attr_init (&attr);

    pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);

    pthread_create (&thread, &attr, &thread_function, NULL);

    pthread_attr_destroy (&attr);

    /* Do work here... */

    /* No need to join the second thread. */

    return 0;

}

 

2、取消线程

  • 一个线程可以调用pthread_cancel来取消另一个线程。

  • 被取消的线程需要被join来释放资源。

  • 被取消的线程的返回值为PTHREAD_CANCELED

  • 有关线程的取消,一个线程可以为如下三个状态:

    • 可异步取消:一个线程可以在任何时刻被取消。

    • 可同步取消:取消的请求被放在队列中,直到线程到达某个点,才被取消。

    • 不可取消:取消的请求被忽略。

    • 默认状态下,线程是可同步取消的。

  • 调用pthread_setcanceltype来设定线程取消的方式:

    • pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

    • pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED, NULL);

    • pthread_setcanceltype (PTHREAD_CANCEL_DISABLE, NULL);

 

3、线程私有数据

  • 每个线程私有数据都有一个key

  • 每个线程都使用此key来访问属于它的那份数据。

  • 调用pthread_key_create来为此线程创建一个新的key和数据。

    • 第一个参数是pthread_key_t

    • 第二个参数是一个回收函数,会在线程退出的时候被调用。

  • 每个线程可以调用pthread_setspecific,根据key来设定线程私有数据的值。

  • 调用pthread_getspecific来得到一个线程私有数据。

 

#include <malloc.h>

#include <pthread.h>

#include <stdio.h>

/* The key used to associate a log file pointer with each thread. */

static pthread_key_t thread_log_key;

/* Write MESSAGE to the log file for the current thread. */

void write_to_thread_log (const char* message)

{

    FILE* thread_log = (FILE*) pthread_getspecific (thread_log_key);

    fprintf (thread_log, “%s\n”, message);

}

/* Close the log file pointer THREAD_LOG. */

void close_thread_log (void* thread_log)

{

    fclose ((FILE*) thread_log);

}

void* thread_function (void* args)

{

    char thread_log_filename[20];

    FILE* thread_log;

    /* Generate the filename for this thread’s log file. */

    sprintf (thread_log_filename, “thread%d.log”, (int) pthread_self ());

    /* Open the log file. */

    thread_log = fopen (thread_log_filename, “w”);

    /* Store the file pointer in thread-specific data under thread_log_key. */

    pthread_setspecific (thread_log_key, thread_log);

    write_to_thread_log (“Thread starting.”);

    /* Do work here... */

    return NULL;

}

int main ()

{

    int i;

    pthread_t threads[5];

    /* Create a key to associate thread log file pointers in thread-specific data. Use close_thread_log to clean up the file

pointers. */

    pthread_key_create (&thread_log_key, close_thread_log);

    /* Create threads to do the work. */

    for (i = 0; i < 5; ++i)

        pthread_create (&(threads[i]), NULL, thread_function, NULL);

        /* Wait for all threads to finish. */

        for (i = 0; i < 5; ++i)

            pthread_join (threads[i], NULL);

    return 0;

}

 

3.1、线程回收

Linux可以提供回收器(cleanup handler),它是一个函数,在线程退出的时候被调用。

调用pthread_cleanup_push可以注册一个回收器。

调用pthread_cleanup_pop可以注销一个回收器。

pthread_cleanup_pop(0)仅仅注销一个回收器。

pthread_cleanup_pop(1)不仅仅注销这个回收器,而且调用它。

#include <malloc.h>

#include <pthread.h>

/* Allocate a temporary buffer. */

void* allocate_buffer (size_t size)

{

    return malloc (size);

}

/* Deallocate a temporary buffer. */

void deallocate_buffer (void* buffer)

{

    free (buffer);

}

void do_some_work ()

{

    /* Allocate a temporary buffer. */

    void* temp_buffer = allocate_buffer (1024);

    /* Register a cleanup handler for this buffer, to deallocate it in case the thread exits or is cancelled. */

    pthread_cleanup_push (deallocate_buffer, temp_buffer);

    /* Do some work here that might call pthread_exit or might be cancelled... */

    /* Unregister the cleanup handler. Because we pass a nonzero value, this actually performs the cleanup by calling

deallocate_buffer. */

    pthread_cleanup_pop (1);

}

 

4、线程同步及临界区

4.1、互斥锁(Mutexes)

  • Mutex全称MUTual EXclusion lock,也即互斥锁。

  • 同一时刻,只有一个线程可以访问互斥锁。

  • 调用pthread_mutex_init可以创建互斥锁:

    • 第一个参数是pthread_mutex_t

    • 第二个参数是互斥锁属性对象,设为NULL表示使用默认属性。

pthread_mutex_t mutex;

pthread_mutex_init (&mutex, NULL);

  • 创建互斥锁的第二种方式:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

  • 调用pthread_mutex_lock来锁定互斥锁。

  • 调用pthread_mutex_unlock来解锁互斥锁。

 

#include <stdio.h>

#include <stdlib.h>

#include <malloc.h>

#include <pthread.h>

struct job

{

    struct job* next;

    int value;

};

struct job* job_queue;

void process_job(struct job*);

pthread_mutex_t job_queue_mutex = PTHREAD_MUTEX_INITIALIZER;

void * process_queue_function (void * arg)

{

    while(1)

    {

        struct job* next_job;

        pthread_mutex_lock(&job_queue_mutex);

        if(job_queue == NULL)

            next_job = NULL;

        else

        {

            printf("begin removing a job...\n");

            next_job = job_queue;

            job_queue = job_queue->next;

            printf("after removing a job...\n");

         }

         pthread_mutex_unlock(&job_queue_mutex);

         if(next_job == NULL)

        {

             sleep(5);

             continue;

        }

        process_job(next_job);

        free(next_job);

    }

    return NULL;

}

void process_job(struct job* p)

{

    printf("The value is : %d.\n", p->value);

}

void enqueue_job(struct job* new_job)

{

    pthread_mutex_lock(&job_queue_mutex);

    printf("begin inserting a job...\n");

    new_job->next = job_queue;

    job_queue = new_job;

    printf("after inserting a job...\n");

    pthread_mutex_unlock(&job_queue_mutex);

}

void * insert_queue_function(void * arg)

{

    int i = 0;

    while(i < 20)

    {

        sleep(1);

        printf("put the value: %d.\n", i);

        struct job* new_job = (struct job*)malloc(sizeof(struct job));

        new_job->next = NULL;

        new_job->value = i;

        enqueue_job(new_job);

        i++;

    }

}

int main()

{

    pthread_t insert_thread, process_thread;

    pthread_create(&insert_thread, NULL, &insert_queue_function, NULL);

    pthread_create(&process_thread, NULL, &process_queue_function, NULL);

    pthread_join(insert_thread, NULL);

    pthread_join(process_thread, NULL);

}

相关推荐

    Linux程序设计权威指南

    目前Linux系统己很普及,但是介绍在Linux上进行开发的书籍并...Linux程序设计权威指南内容包括Linux开发环境、Linux编程的入门知识、系统和网络编程、多线程程序设计、控制台编程、XWindow系统编程、国际化编程知识等。

    Linux程序设计中文第4版.part2

     1.2 Linux程序设计  1. 2.1 Linux程序  1. 2.2 文本编辑器  1. 2.3 C语言编译器  1. 2.4 开发系统导引  1.3 获得帮助  1.4 小结 第2章 shell程序设计  2.1 为什么使用shell编程  ...

    Linux程序设计中文第4版.part3

     1.2 Linux程序设计  1. 2.1 Linux程序  1. 2.2 文本编辑器  1. 2.3 C语言编译器  1. 2.4 开发系统导引  1.3 获得帮助  1.4 小结 第2章 shell程序设计  2.1 为什么使用shell编程  ...

    Linux程序设计中文第4版.part1

     1.2 Linux程序设计  1. 2.1 Linux程序  1. 2.2 文本编辑器  1. 2.3 C语言编译器  1. 2.4 开发系统导引  1.3 获得帮助  1.4 小结 第2章 shell程序设计  2.1 为什么使用shell编程  2.2 一点...

    Linux程序设计中文第四版

    Linux程序设计中文第四版,找了很久得到的,主要内容有shell脚本,多线程,makefile编写,socket编程,mysql的连接

    Linux程序设计 第4版.haozip01

    Linux程序设计 分卷文件共有以下2个: Linux程序设计 第4版.haozip01.zip Linux程序设计 第4版.haozip02.zip 基本信息 原书名: Beginning Linux Programming 原出版社: Wrox 作者: (英)Neil Matthew Richard ...

    Linux程序设计 第4版.haozip02

    Linux程序设计 分卷文件共有以下2个: Linux程序设计 第4版.haozip01.zip Linux程序设计 第4版.haozip02.zip 基本信息 原书名: Beginning Linux Programming 原出版社: Wrox 作者: (英)Neil Matthew Richard ...

    linux多线程编程

    第四章 互斥量 39 一、什么是互斥锁 39 二、初始化/回收互斥锁 40 三、对互斥量加减锁 40 四、互斥锁属性 45 五、应用互斥量需要注意的几点 48 第五章 条件变量 48 一、什么是条件变量 48 二、条件变量函数 48 三、...

    Linux C程序设计大全

    第4章 C语言中的指针与字符串 4.1 sizeof运算符 4.1.1 sizeof运算符的应用——得到内置类型的大小 4.1.2 sizeof运算符的应用——得到复合类型的大小 4.2 指针的应用 4.2.1 指针与别名陷阱 4.2.2 数组的指针 4.2.3 ...

    嵌入式Linux程序设计案例与实验教程(配套光盘)第一部分

    第4章 嵌入式Linux接口设计与驱动程序53 4.1 驱动程序设计基础53 4.1.1 Linux驱动程序简介53 4.1.2 开发驱动程序的方法53 4.1.3 设备驱动程序的分类53 4.1.4 主设备号和次设备号54 4.1.5 设备文件系统(devfs...

    嵌入式Linux程序设计案例与实验教程(配套光盘)第二部分

    第4章 嵌入式Linux接口设计与驱动程序53 4.1 驱动程序设计基础53 4.1.1 Linux驱动程序简介53 4.1.2 开发驱动程序的方法53 4.1.3 设备驱动程序的分类53 4.1.4 主设备号和次设备号54 4.1.5 设备文件系统(devfs...

    嵌入式Linux程序设计案例与实验教程(配套光盘)第三部分

    第4章 嵌入式Linux接口设计与驱动程序53 4.1 驱动程序设计基础53 4.1.1 Linux驱动程序简介53 4.1.2 开发驱动程序的方法53 4.1.3 设备驱动程序的分类53 4.1.4 主设备号和次设备号54 4.1.5 设备文件系统(devfs...

    Linux网络编程 part2

    part 2 本书内容分为4个部分:linux程序设计基础部分、linux用户空间网络编程部分、linux内核网络编程部分以及综合案例部分。内容包含linux系统概述、linux编程环境、linux文件系统简介、linux下的进程和线程、tcp/...

    Linux进程和线程的基本编程、通讯和例程1

    设计模式 POSIX多线程程序设计(第4章:使用线程的几种方式)瓦釜苑-CSDN博客posix多线程程序设计。调试相关:Linux进程崩溃原调试_guotian

    Linux高性能服务器编程PDF带目录高清版

    第二部分对高性能服务器编程的核心要素进行了全面深入的剖析,包含Linux网络编程API、高级I/O函数、Linux服务器程序规范、高性能服务器程序框架、I/O复用、信号、定时器、高性能I/O框架库Libevent、多进程编程、多...

    嵌入式Linux程序设计案例与实验教程-实例代码

    第4章 嵌入式Linux接口设计与驱动程序53 4.1 驱动程序设计基础53 4.1.1 Linux驱动程序简介53 4.1.2 开发驱动程序的方法53 4.1.3 设备驱动程序的分类53 4.1.4 主设备号和次设备号54 4.1.5 设备文件系统...

    嵌入式Linux C语言应用程序设计

    pointer2.c是6.2.2的第二个源代码,pointer3.c是6.2.2的第三个源代码,pointer4.c是6.2.3的第一个源代码,pointer5.c是6.2.3的第二个源代码,pointer6.c是6.2.3的第三个源代码,pointer7.c是6.2.3的第四个源代码。...

    linux系统编程之线程.zip

    Linux下: 线程:最小的执行单位 进程:最小分配资源单位,可看成是只有一个线程的进程。 Linux内核线程实现原理 类Unix系统中,早期是没有“线程”概念的,80年代才引入,借助进程机制实现出了线程的概念。...

    linux+C程序设计.pdf

    linux+C程序设计.pdf 目 录 第一章 基础知识 第二章 进程介绍 第三章 文件操作 第四章 时间概念 第五章 信号处理 第六章 消息管理 第七章 线程操作 第八章 网络编程 第九章 Linux 下C 开发工具介绍

Global site tag (gtag.js) - Google Analytics