zoukankan      html  css  js  c++  java
  • Chapter11 线程

    1. void 和void* 的使用

        void 意思是无类型,void* 意思是无类型指针,可以指向所有数据类型。

        (1) void 主要用于对函数参数和返回值的限定,加上void 限定则表明该函数不接受参数或者无返回值(因为在C语言中,即使函数声明时无参数,在函数调用时传进参数是不会报错的,即使函数没有指定返回类型,函数会默认返回int类型,所以加上void 限定是十分有必要的)

        (2) void* 则多用于函数参数(或返回值)可以是任意类型的情况下,因为void* 可以接受任意类型而无需强制装换,但将其转换为具体类型则需要强制装换,这有点类似于C++中的继承关系。

    2. 线程创建与线程标识

        线程ID 用pthread_t 数据结构来表示,在不同的操作系统中表示方法不同,所以可移植的操作系统实现不能把它作为整数处理,因此必须使用 pthread_equal() (函数原型:int pthread_equal(pthread_t tid1, pthread_t tid2);)函数来对像个线程ID 进行比较。调用 pthread_self 函数可以获取自身线程ID。

        在传统Unix进程模型中,每个进程只有一个线程。在POSIX线程的情况下, 当程序开始运行时,也是以单进程中的单个控制线程启动的,并可以通过pthread_create函数创建新线程。(函数原型: int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rth)(void*), void *restrict arg);) 其中tidp为 ID,attr为订制线程属性,start_rth 是线程开始运行的函数地址,arg是该函数的参数(若由多个参数,则使用结构体)

        《APUE》 程序清单 11-1 打印线程ID:

     1 #include "apue.h"
     2 #include <pthread.h>
     3 
     4 pthread_t ntid;
     5 
     6 void printids(const char* s) {
     7     pid_t     pid;
     8     pthread_t tid;
     9 
    10     pid = getpid();
    11     tid = pthread_self();
    12     printf("%s pid %u tid %u (0x%x)
    ", s, (unsigned int)pid,
    13         (unsigned int)tid, (unsigned int)tid);
    14 }
    15 
    16 void* thr_fn(void* arg) {
    17     printids("new thread: ");
    18     return ((void*)0);
    19 }
    20 
    21 int main()
    22 {
    23     int err;
    24     err = pthread_create(&ntid, NULL, thr_fn, NULL);
    25     if (err != 0)
    26         err_quit("can't create thread: %s
    ", strerror(err));
    27     printids("main thread: ");
    28 
    29     sleep(1);
    30     exit(0);
    31 }

        程序运行结果:

         main thread:  pid 20961 tid 4253615936 (0xfd890740)
         new thread:  pid 20961 tid 4245337856 (0xfd0ab700)
       进程ID 相同, 线程ID 不同

    3. 线程终止

        终止线程有三种方式,在不终止整个进程的情况下终止它的控制流(注意进程中任一线程调用了exit, _Exit 或者_exit ,那么整个进程就会终止)

    • 线程只是从启动历程中返回,返回值是线程的退出码 (return)
    • 线程可以被同一进程中的其他线程取消。(int pthread_cancel(phread_ tid);)
    • 线程调用pthread_exit. (函数原型: void pthread_exit(void *rval_ptr);  )

        有时程序要了保证在主进程结束前线程运行结束,会在主进程中调用sleep。也可以使用pthread_join (函数原型: int pthread_join(pthread_t thread, void** rval_ptr);  )

        函数,调用该函数时,调用线程会一直阻塞,直到指定的线程调用pthread_exit,从启动例程中返回或者别取消。另外通过pthread_join函数还可以访问到 pthread_exit()

        参数中的rval_ptr 指针。

    4. 线程清理处理程序(thread cleanup handler)

        线程可以安排它退出时需要调用的函数,这些处理程序记录在栈中,可以使用pthread_cleanup_push() 函数注册多个清理处理程序。

        在下面三种情况下,已经注册的清理处理程序将会被调用

    •     调用pthread_exit时 (注意return时不会调用)
    •     相应取消请求时
    •     用非零参数调用pthread_cleanup_pop 时 (与pthread_cleanup_push相对应)

    5. 线程同步

        在变量修改时间多于一个存储器(非原子性)访问周期的处理器结构中,且当存储器读与存储器写这两个周期交叉时,潜在的不一致性就会出现。如果修改操作是原子操作,那么就不存在竞争。

    *互斥量:可以通过pthread的虎吃接口保护数据,确保同一时间只有一个线程访问数据。

        pthread_mutex_t   互斥变量数据类型

        pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t * restrict attr) 对互斥量进行初始化,attr = NULL 则使用默认属性;也可使用常量     

                    PTHREAD_MUTEX_INITIALIZER 进行初始化

        pthread_mutex_destroy(pthread_mutex_t* mutex); 动态分配的互斥量(如malloc),释放内存前要调用该函数

        pthread_mutex_lock(phtread_mutex_t* mutex); 对互斥量进行加锁

        pthread_mutex_unlock(phtread_mutex_t* mutex); 对互斥量解锁

        pthread_mutex_trylock(pthread_mutex_t* mutex) 尝试加锁,未阻塞返回0, 否则失败返回EBUSY

    * 避免死锁

        死锁可以通过小心地控制互斥量加锁的顺序来避免。

        以下代码中,每个foo 结构都由 一个f_lock 互斥量来保护该数据结构的引用次数,另外还有一个hashlock 来维护一个用于跟踪foo数据结构的散列列表,这里为了避免死锁需要小心地维护f_lock 和hashlock 的加锁顺序。

        《APUE》程序清单 11-6 使用两个互斥量

    #include <stdlib.h>
    #include <pthread.h>
    
    #define NHASH 29
    #define HASH(fp) (((unsigned long)fp)%NHASH)
    
    struct foo* fh[NHASH];
    
    pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER;
    
    struct foo {
        int             f_count;
        pthread_mutex_t f_lock;
        struct foo*     f_next;
        int             f_id;
    };
    
    struct foo* foo_alloc(void)
    {
        struct foo* fp;
        int    idx;
    
        if ((fp = malloc(sizeof(struct foo))) != NULL) {
            fp->f_count = 1;
            if (pthread_mutex_init(&fp->f_lock, NULL) != NULL) {
                free(fp);
                return (NULL);
            }
            idx = HASH(fp);
            pthread_mutex_lock(&hashlock);
            fp->f_next = fh[idx];
            fh[idx] = fp;
            pthread_mutex_lock(&fp->f_lock);
            pthread_mutex_unlock(&hashlock);
    
            pthread_mutex_unlock(&fp->f_lock);
        }
        return (fp);
    }
    
    void foo_hold(struct foo* fp)
    {
        pthread_mutex_lock(&fp->f_lock);
        fp->f_count++;
        pthread_mutex_unlock(&fp->f_lock);
    }
    
    struct foo* foo_find(int id)
    {
        struct foo* fp;
        int    idx;
    
        idx = HASH(fp);
        pthread_mutex_lock(&hashlock);
        for (fp = fh[idx]; fp != NULL; fp = fp->f_next) {
            if (fp->f_id == id) {
                foo_hold(fp);
                break;
            }
        }
        pthread_mutex_unlock(&hashlock);
        return (fp);
    }
    
    void foo_rele(struct foo* fp)
    {
        struct foo* tfp;
        int    idx;
    
        pthread_mutex_lock(&fp->f_lock);
        if (fp->f_count == 1) {
            pthread_mutex_unlock(&fp->f_lock);
            pthread_mutex_lock(&hashlock);
            pthread_mutex_lock(&fp->f_lock);
    
            /* need to recheck the condition */
            if (fp->f_count != 1) {
                fp->f_count--;
                pthread_mutex_unlock(&fp->f_lock);
                pthread_mutex_unlock(&hashlock);
                return;
            }
    
            /* remove from list */
            idx = HASH(fp);
            tfp = fh[idx];
            if (tfp == fp) {
                fh[idx] = fp->f_next;
            } else {
                while (tfp != fp)
                    tfp = tfp->f_next;
                tfp->f_next = fp->f_next;
            }
    
            pthread_mutex_unlock(&hashlock);
            pthread_mutex_unlock(&fp->f_lock);
            pthread_mutex_destory(&fp->f_lock);
        } else {
            fp->f_count--;
            pthread_mutex_unlock(&fp->f_lock);
        }
    }

        程序11-6 可以只通过hashlock 来保护引用计数来使事情大大简化,而结构互斥量f_lock 则可用来保护foo结构中的其他任何东西。

  • 相关阅读:
    ZOJ 3765 Lights (zju March I)伸展树Splay
    UVA 11922 伸展树Splay 第一题
    UVALive 4794 Sharing Chocolate DP
    ZOJ 3757 Alice and Bod 模拟
    UVALive 3983 捡垃圾的机器人 DP
    UVA 10891 SUM游戏 DP
    poj 1328 Radar Installatio【贪心】
    poj 3264 Balanced Lineup【RMQ-ST查询区间最大最小值之差 +模板应用】
    【转】RMQ-ST算法详解
    poj 3083 Children of the Candy Corn 【条件约束dfs搜索 + bfs搜索】【复习搜索题目一定要看这道题目】
  • 原文地址:https://www.cnblogs.com/Patrickcxt/p/3918881.html
Copyright © 2011-2022 走看看