Loading

thread.md

多线程

Hello world线程

  1. 线程创建pthread_create()

    int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                              void *(*start_routine) (void *), void *arg);
        /*
        一个线程变量名,被创建线程的标识
        线程的属性指针,缺省为NULL即可
        被创建线程的程序代码
        程序代码的参数,For example: - pthread_t thrd1; - pthread_attr_t *attr; - void thread_function(void *argument); - char *some_argument;
        */
    
  2. 线程结束pthread_exit()

    pthread_exit(void *retval);
    // pthread_exit函数唯一的参数value_ptr是函数的返回代码,只要pthread_join中的第二个参数value_ptr不是NULL,这个值将被传递给value_ptr。
    
  3. 线程等待 pthread_join()
    int pthread_join( pthread_t  thread, void * * value_ptr );
    // 阻塞函数,调用它的函数将一直等到被等待的线程结束为止。如果value_ptr不为NULL,那么线程thread的返回值存储在该指针指向的位置。该返回值可以是由pthread_exit给出的值,或者该线程被取消而返回PTHREAD_CANCELED。
    

实例

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

void *print_msg_thread(void *ptr);

int main(void)
{
    pthread_t t1, t2;
    char msg1[] = "One", msg2[] = "Two";

    if (pthread_create(&t1, NULL, print_msg_thread,msg1) == 0){
        printf("线程1创建成功\n");
    }else{
        printf("线程1创建失败");
    }
    if (pthread_create(&t2, NULL, print_msg_thread,msg2) == 0){
        printf("线程2创建成功\n");
    }else{
        printf("线程2创建失败");
    }

    void *retval;
    int tmp1, tmp2;

    tmp1 = pthread_join(t1, &retval);
    printf("thread1 return value(retval) %p\n", retval);
    printf("thread1 return value(tmp) is %d\n", tmp1);
    printf("thread1 end\n");

    tmp2 = pthread_join(t2, &retval);
    printf("thread2 return value(retval) %p\n",             retval);
    printf("thread2 return value(tmp) is %d\n", tmp2);
    printf("thread2 end\n");

    return 0;
}
void *print_msg_thread(void *ptr)
{
    for (int i = 0; i < 5; i++)
    {
        printf("%s: %d\n", ptr, i);
    }
    
}

多线程的同步与互斥

  1. 创建

    • 静态创建: POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁,方法如下:

      pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

    • 动态创建: pthread_mutex_init()函数来初始化互斥锁

      int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)

      mutuex用于指定锁的属性

      互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。当前(glibc2.2.3,linuxthreads0.9)有四个值可供选择:

      Name 简述 介绍
      PTHREAD_MUTEX_TIMED_NP 缺省值,普通锁 当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
      PTHREAD_MUTEX_RECURSIVE_NP 嵌套锁 允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
      PTHREAD_MUTEX_ERRORCHECK_NP 检错锁 如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样保证当不允许多次加锁时不出现最简单情况下的死锁。
      PTHREAD_MUTEX_ADAPTIVE_NP 适应锁 动作最简单的锁类型,仅等待解锁后重新竞争
  2. 销毁

    pthread_mutex_destroy()用于注销一个互斥锁,API定义如下:

    int pthread_mutex_destroy(pthread_mutex_t *mutex)

    ​ 销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的 pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。

  3. 其他

     锁操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个,不论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁。对于普通锁和适应锁类型,解锁者可以是同进程内任何线程;而检错锁则必须由加锁者解锁才有效,否则返回EPERM;对于嵌套锁,文档和实现要求必须由加锁者解锁,但实验结果表明并没有这种限制,这个不同目前还没有得到解释。在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。
    

      int pthread_mutex_lock(pthread_mutex_t *mutex)
      int pthread_mutex_unlock(pthread_mutex_t *mutex)
      int pthread_mutex_trylock(pthread_mutex_t *mutex)

      pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。

  4. 死锁

    总体来讲, 有几个不成文的基本原则:

    • 共享资源操作前一定要获得锁。
    • 完成操作以后一定要释放锁。
    • 尽量短时间地占用锁。
    • 如果有多锁, 如获得顺序是ABC连环扣, 释放顺序应该是CBA。
    • 线程错误返回时应该释放它所获得的锁。

信号量

/usr/include/semaphore.h中进行定义的,信号量的数据结构为sem_t, 本质上,它是一个long型整数

  1. 初始化

    int sem_init(sem_t *sem, int pshared, unsigned int value);

    返回值: 成功0, 失败-1, errno被设置

    参数:

    • sem,信号地址
    • pshared: 不是0的时候,该信号量在进程间共享,否则只能为当前进程的所有线程们共享
    • value:信号量的初始值
  2. 信号量操作

    int sem_wait(sem_t *sem) //信号减一,信号 == 1 时,会阻塞

    int sem_post(sem_t *sem)//信号+1

  3. 销毁

    int sem_destroy(sem_t *sem);

posted @ 2022-05-28 17:40  nsfoxer  阅读(22)  评论(0编辑  收藏  举报