Linux下的线程

一、线程的优点

与传统进程相比,用线程来实现相同的功能有如下优点:

(1)系统资源消耗低。

(2)速度快。

(3)线程间的数据共享比进程间容易的多。

二、多线程编程简单实例

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>


void
thread1_routine(void)
{
    printf("new thread:thread_id is %u, process_id is %u
",
         pthread_self(), getpid());
}
void
thread2_routine(void)
{
    printf("new thread:thread_id is %u, process_id is %u
",
         pthread_self(), getpid());
}

int
main(void)
{
    pthread_t pt;
    printf("old thread:thread_id is %u, process_id is %u
",
        pthread_self(), getpid()); 
    pthread_create(&pt, NULL, (void *)thread1_routine, NULL);
    pthread_create(&pt, NULL, (void *)thread2_routine, NULL);
    usleep(5);
    return(0);
}

运行结果如下(可以看出在同一个进程中有三个不同的线程同时在运行):

image

image

三、线程属性

image

线程属性包括绑定属性、分离属性、堆栈地址、堆栈大小和优先级。其中分离属性、堆栈地址以及堆栈大小的介绍可参考http://www.cnblogs.com/nufangrensheng/p/3522583.html。系统默认的是非邦定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。在pthread_create中,把第二个参数设置为NULL的话,将采用默认的属性配置。

1、绑定属性

线程可以分为用户级线程和内核级线程两种(可参考http://blog.csdn.net/songjinshi/article/details/9042265以及http://www.xuebuyuan.com/1380720.html),而绑定属性正是设置用户级线程和内核级线程之间的关系。

绑定属性分为两种:绑定和非绑定。在绑定属性下,一个用户级线程固定分配给一个内核线程,因为CPU时间片的调度是面向内核线程(轻量级进程)的,因此具有 绑定属性的线程可以保证在需要的时候总有一个内核线程与之对应。在非绑定属性下,用户线程和内核线程的关系不是始终固定的,而是由系统根据实际情况分配的。

image

2、优先级

image

image

四、线程互斥

image

image

image

生产者消费者实例(多线程+互斥量):

#include <stdio.h>
#include <pthread.h>
#include <sched.h>

void *producter_f(void *arg);
void *consumer_f(void *arg);

int buffer_has_item = 0;
pthread_mutex_t mutex;
int running = 1;

int 
main(void)
{
    pthread_t consumer_t;
    pthread_t producter_t;

    pthread_mutex_init(&mutex, NULL);

    pthread_create(&producter_t, NULL, (void *)producter_f, NULL);

    pthread_create(&consumer_t, NULL, (void *)consumer_f, NULL);

    sleep(1);      /* 等待线程创建完毕 */
    running = 0;
    pthread_join(consumer_t, NULL);
    pthread_join(producter_t, NULL);
    pthread_mutex_destroy(&mutex);

    return(0);
}

void *
producter_f(void *arg)
{
    while(running)
    {
        if(buffer_has_item < 10) /* 最多允许生产10个 */
        {
            pthread_mutex_lock(&mutex);
            buffer_has_item++;
            printf("product, total: %d
", buffer_has_item);
            pthread_mutex_unlock(&mutex);
        }
    }
}

void *
consumer_f(void *arg)
{
    while(running)
    {
        if(buffer_has_item > 0)   /* 缓冲区为空时不允许再消费 */
        {
            pthread_mutex_lock(&mutex);
            buffer_has_item--;
            printf("consume, total: %d
", buffer_has_item);
            pthread_mutex_unlock(&mutex);
        }
    }
}

编译运行结果如下:

image

image

 

五、线程中使用信号量

(此处使用的信号量是POSIX无名信号量:http://www.cnblogs.com/nufangrensheng/p/3564306.html

线程的信号量与进程的信号量类似,使用线程的信号量可以高效地完成基于线程的资源计数。信号量实际上是一个非负的整数计数器,用来实现对公共资源的控制。在公共资源增加的时候,信号量的值增加;公共资源消耗的时候,信号量的值减少;只有当信号量的值大于0时,才能允许访问信号量所代表的公共资源。

image

image

image

image

image

生产者消费者实例(多线程+信号量):

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>

void *producter_f(void *arg);
void *consumer_f(void *arg);

sem_t sem;
int running = 1;

int 
main(void)
{
    pthread_t consumer_t;
    pthread_t producter_t;
    
    sem_init(&sem, 0, 16);

    pthread_create(&producter_t, NULL, (void *)producter_f, NULL);

    pthread_create(&consumer_t, NULL, (void *)consumer_f, NULL);

    sleep(1);
    running = 0;
    pthread_join(consumer_t, NULL);
    pthread_join(producter_t, NULL);
    sem_destroy(&sem);

    return(0);
}

void *
producter_f(void *arg)
{
    int semval = 0;
    while(running)
    {
        usleep(1);
        sem_post(&sem);
        sem_getvalue(&sem, &semval);
        printf("product, total: %d
", semval);
    }
}

void *
consumer_f(void *arg)
{
    int semval = 0;
    while(running)
    {
        usleep(1);
        sem_wait(&sem);
        sem_getvalue(&sem, &semval);
        printf("consume, total: %d
", semval);
    }
}
编译运行如下:

image

image

image

 

更多关于线程的介绍可参考http://www.cnblogs.com/nufangrensheng/p/3518114.html及其后续博文。

原文地址:https://www.cnblogs.com/nufangrensheng/p/3581962.html