Thread编程-C

线程与进程关系

  • 轻量级进程(light-weightprocess),也有PCB,创建线程使用底层函数和进程一样,都是clone.

  • 从内核里看进程和线程是一样的,都有各自不同的PCB,但是PCB中指向内存资源的三级页表示相同的

  • 进程可以蜕变成线程

  • 在美国人眼里,线程就是寄存器和栈

  • 在Linux下,线程是最小的执行单位;进程是最小的分配资源的单位

进程原语线程原语描述
forkpthread_create创建新的控制流
exitpthread_exit从现有的控制流中退出
waitpidpthread_join从控制流中得到退出状态
atexitpthread_cancel_push注册在退出控制流时调用的函数
getpidpthread_self获取控制流的ID
abortphtead_cancle请求控制流的正常退出

查看线程号

$ps -eLf
UID        PID  PPID   LWP  C NLWP STIME TTY          TIME CMD
root         1     0     1  0    1 May17 ?        00:00:02 /sbin/init
root         2     0     2  0    1 May17 ?        00:00:00 [kthreadd]
root         3     2     3  0    1 May17 ?        00:00:16 [ksoftirqd/0]
root         5     2     5  0    1 May17 ?        00:00:00 [kworker/0:0H]
root         7     2     7  0    1 May17 ?        00:01:55 [rcu_sched]

LWP列表示的就是线程号

查看具体线程信息

$ps -Lf 7
UID        PID  PPID   LWP  C NLWP STIME TTY      STAT   TIME CMD
root         7     2     7  0    1 May17 ?        S      1:57 [rcu_sched]

线程的本质

其实在Linux中,新建的线程并不是在原先的进程中,而是系统通过 一个系统调用clone()。该系统copy了一个和原先进程完全一样的进程, 并在这个进程中执行线程函数。不过这个copy过程和fork不一样。 copy后的进程和原先的进程共享了所有的变量,运行环境。 这样,原先进程中的变量变动在copy后的进程中便能体现出来。

pthread_create


#include <pthread.h>

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

参数

第一个参数为指向线程标识符的指针。

第二个参数用来设置线程属性。

第三个参数是线程运行函数的起始地址。

最后一个参数是运行函数的参数。


/**********************************************************
 * Author        : yxmsw2007
 * Email         : yxmsw2007@gmail.com
 * Last modified : 2015-05-19 22:34
 * Filename      : thread_example.c
 * Description   : gcc -lpthread thread_example.c -o thread_example
 * *******************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <time.h>

#define     MAX 3

int number = 0;

pthread_t id[2];

pthread_mutex_t mut; //初始化静态互斥锁

void thread1(void) {
    int i;
    printf("Hello, I am pthread1!\n");
    for (i = 0; i < MAX; i++)
    {
        pthread_mutex_lock(&mut); //此处上锁,保证number的唯一性
        number++;
        printf("THread1: number = %d\n", number);
        pthread_mutex_unlock(&mut);
        sleep(1); //linux c下sleep(minute),里面单位是分钟
    }
    pthread_exit(NULL); //线程通过执行此函数,终止执行。返回是一个空指针类型
}

void thread2(void) {
    int i;
    printf("Hello, I am pthread2!\n");
    for (i = 0; i < MAX; i++)
    {
        pthread_mutex_lock(&mut); //此处上锁,保证number的唯一性
        number++;
        printf("THread2: number = %d\n", number);
        pthread_mutex_unlock(&mut);
        sleep(1); //linux c下sleep(minute),里面单位是分钟
    }
    pthread_exit(NULL); //线程通过执行此函数,终止执行。返回是一个空指针类型
}

void thread_create(void) {
    int temp;
    memset(&id, 0, sizeof(id));
    if (temp = pthread_create(&id[0], NULL, (void*)thread1, NULL) != 0)
    {
       printf("Thread1 fail to create!\n"); 
    } else {
        printf("Thread 1 create!\n");
    }
    if (temp = pthread_create(&id[1], NULL, (void*)thread2, NULL) != 0)
    {
        printf("Thread 2 fail to create!\n");
    } else {
        printf("Thread 2 create!\n");
    }
}

void thread_wait() {
    if (id[0] != 0)
    {
        pthread_join(id[0], NULL);
        printf("Thread 1 completed!\n");
    }
    if (id[1] != 0)
    {
        pthread_join(id[1], NULL);
        printf("Thread 2 completed!\n");
    }
}

int main(int argc, char *argv[])
{
    int i, ret1, ret2;
    pthread_mutex_init(&mut, NULL); //动态互斥锁
    printf("Main function, creating thread...\n");
    thread_create();
    printf("Main function, waiting for the pthread end!\n");
    thread_wait();
    // 代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。
    // 加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。
    return (0);
}

线程退出

单个线程可以通过三种方式退出,在不终止整个进程的情况下停止它的控制流。

(1)线程只是从启动全程中返回(return方式),返回值是线程的退出码。

(2)线程可以被同一进程中的其他线程取消

(3)线程调用pthread_exit

pthread_exit


#include <pthread.h>

void pthread_exit(void *retval);

retval:调用者线程的返回值,可由其他函数如pthread_join来检索获取。

pthread_join


#include <pthread.h>

int pthread_join(pthread_t thread, void **retval);

thread: 等待线程的标识符

thread_return:用户定义的指针,用来存储被等待线程的返回值(不为NULL时)

pthread_cancel


#include <pthread.h>

int pthread_cancel(pthread_t thread);

pthread_setcancelstate与pthread_setcanceltype


#include <pthread.h>

int pthread_setcancelstate(int state, int *oldstate);

int pthread_setcanceltype(int type, int *oldtype);


/**********************************************************
 * Author        : yxmsw2007
 * Email         : yxmsw2007@gmail.com
 * Last modified : 2015-05-20 00:23
 * Filename      : pthread_cancel_setcancelstate.c
 * Description   : gcc -lpthread pthread_cancel_setcancelstate.c -o pthread_cancel_setcancelstate
 * *******************************************************/
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>

#define handle_error_en(en, msg) \
    do { errno = en; perror(msg); exit(EXIT_FAILURE);  } while (0)

static void *thread_func(void *ignored_argument)
{
    int s;

    /* Disable cancellation for a while, so that we don't
     *               immediately react to a cancellation request */

    s = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
    if (s != 0)
        handle_error_en(s, "pthread_setcancelstate");

    printf("thread_func(): started; cancellation disabled\n");
    sleep(5);
    printf("thread_func(): about to enable cancellation\n");

    s = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    if (s != 0)
        handle_error_en(s, "pthread_setcancelstate");

    /* sleep() is a cancellation point */

    sleep(1000);        /* Should get canceled while we sleep */

    /* Should never get here */

    printf("thread_func(): not canceled!\n");
    return NULL;

}

int main(void)
{
    pthread_t thr;
    void *res;
    int s;

    /* Start a thread and then send it a cancellation request */

    s = pthread_create(&thr, NULL, &thread_func, NULL);
    if (s != 0)
        handle_error_en(s, "pthread_create");

    sleep(2);           /* Give thread a chance to get started */

    printf("main(): sending cancellation request\n");
    s = pthread_cancel(thr);
    if (s != 0)
        handle_error_en(s, "pthread_cancel");

    /* Join with thread to see what its exit status was */

    s = pthread_join(thr, &res);
    if (s != 0)
        handle_error_en(s, "pthread_join");

    if (res == PTHREAD_CANCELED)
        printf("main(): thread was canceled\n");
    else
        printf("main(): thread wasn't canceled (shouldn't happen!)\n");
    exit(EXIT_SUCCESS);

}

pthread_cleanup_push与pthread_cleanup_pop


#include <pthread.h>

void pthread_cleanup_push(void (*routine)(void *), void *arg);

void pthread_cleanup_pop(int execute);

清理函数rtn的调用顺序是由pthread_clean_push函数安排的。

它在下列几种情况下执行:

  • 调用pthread_exit时

  • 响应取消请求时

  • 用非零execute参数调用pthread_clean_pop时

如果execute参数为0,清理函数将不被调用。无论何种情况,pthread_clean_pop都将删除上次pthread_clean_push建立的清理处理程序。

如果线程使用return从例程返回,那么pthread_clean_push建立的清理处理程序不会被执行。


/**********************************************************
 * Author        : yxmsw2007
 * Email         : yxmsw2007@gmail.com
 * Last modified : 2015-05-20 00:23
 * Filename      : pthread_cleanup_push_pop.c
 * Description   : gcc -lpthread pthread_cleanup_push_pop.c -o pthread_cleanup_push_pop
 * *******************************************************/
#include <pthread.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#define handle_error_en(en, msg) \
    do { errno = en; perror(msg); exit(EXIT_FAILURE);  } while (0)

static int done = 0;
static int cleanup_pop_arg = 0;
static int cnt = 0;

static void cleanup_handler(void *arg)
{
    printf("Called clean-up handler\n");
    cnt = 0;

}

static void *thread_start(void *arg)
{
    time_t start, curr;

    printf("New thread started\n");

    pthread_cleanup_push(cleanup_handler, NULL);

    curr = start = time(NULL);

    while (!done) {
        pthread_testcancel();           /* A cancellation point */
        if (curr < time(NULL)) {
            curr = time(NULL);
            printf("cnt = %d\n", cnt);  /* A cancellation point */
            cnt++;

        }

    }

    pthread_cleanup_pop(cleanup_pop_arg);
    return NULL;

}

int main(int argc, char *argv[])
{
    pthread_t thr;
    int s;
    void *res;

    s = pthread_create(&thr, NULL, thread_start, NULL);
    if (s != 0)
        handle_error_en(s, "pthread_create");

    sleep(2);           /* Allow new thread to run a while */

    if (argc > 1) {
        if (argc > 2)
            cleanup_pop_arg = atoi(argv[2]);
        done = 1;


    } else {
        printf("Canceling thread\n");
        s = pthread_cancel(thr);
        if (s != 0)
            handle_error_en(s, "pthread_cancel");

    }

    s = pthread_join(thr, &res);
    if (s != 0)
        handle_error_en(s, "pthread_join");

    if (res == PTHREAD_CANCELED)
        printf("Thread was canceled; cnt = %d\n", cnt);
    else
        printf("Thread terminated normally; cnt = %d\n", cnt);
    exit(EXIT_SUCCESS);

}

源码下载

SampleC-CPP

参考资料

linux c学习笔记----线程创建与终止

POSIX 线程详解

线程概念:线程和进程之间的关系,线程间可共享资源,线程间非共享资源,线程的优缺点

linux c多线程编程实例代码

pthread_join函数及linux线程

转载于:https://my.oschina.net/noke/blog/1507363

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值