Linux开发入门笔记——线程编程

版权声明:本文为Trinity原创文章,未经Trinity允许不得转载 https://blog.csdn.net/Caoyang_He/article/details/85234552

什么是线程

线程通常叫做轻型级进程。线程是在共享内存空间中并发执行的多道执行路径,他们共享一个进程的资源。

创建进程、线程是有区别的。新进程运行时间独立,执行时几乎独立于创建它的进程;而新线程拥有自己的堆栈、代码,但却与创建者共享全局变量等。

进程与线程的关系
在这里插入图片描述
线程的优缺点

  • 优点
    • 创建一个新线程的代价要比创建一个新进程小得多。
    • 让一个程序同时做两件事情是很有用的,即提高效率,又降低成本
  • 缺点
    • 多线程程序编写中,由于时间偏差、共享了不该共享的变量而造成不良影响的可能性很大
    • 将运算通过两个线程的程序在一台单处理器上运行不见得很快。

线程分类

  • 用户级线程
    主要解决的是上下文切换的问题,其调度算法和调度过程全部有用户决定。
  • 内核级线程
    有内核调度机制实现。
  • 两者关系
    现在大多数操作系统都采用用户级线程和内核级线程并存的方法。用户级线程可与内核级线程实现“一对一”,“一对多”的对应关系。

线程的实现

线程实现—创建

  • 头文件:#include <pthread.h>
  • 原型
    int pthread_create(pthread_t *thread,
    thread_attr_t *attr,
    void *(*start_routine)(void *),
    void *arg)
    thread:线程标识符
    attr:线程属性设置,通常NULL
    start_routine:线程函数起始地址
    arg:传递给start_routine的参数

线程实现—退出

  • 头文件:#include <pthread.h>
  • void pthread_exit( void *retval )
    retval: pthread_exit调用者线程的返回值,可由其他函数和pthread_join来检测获取。

线程实现—等待

  • 头文件:#include <pthread.h>
  • pthread_join( pthread_t *th,
    void **thread_return )
    th:等待线程的标识符
    thread_return:用户定义指针,用来存储被等待线程的返回值——*retval

第一个线程例子

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
void *thread_function(void *arg);
char message[] = "Hello World";
int main() {
    int res;
    pthread_t a_thread;
    void *thread_result;
    res = pthread_create(&a_thread, NULL, 
		thread_function, (void *)message);
    if (res != 0) {
        perror("Thread creation failed");
        exit(EXIT_FAILURE);
    }
    printf("Waiting for thread to finish...\n");
	res = pthread_join(a_thread,&thread_result);
	if (res != 0) {
	       perror("Thread join failed");
	       exit(EXIT_FAILURE);
	   }
	printf("Thread joined, it returned %s\n",
				 (char *)thread_result);
	printf("Message is now %s\n", message);
	exit(EXIT_SUCCESS);
	}
	
	void *thread_function(void *arg) {
	printf("thread_function is running. Argument 
			was %s\n", (char *)arg);
	sleep(3);
	strcpy(message, "Bye!");
	pthread_exit("Thank you for the CPU time");
}


线程的属性

线程与私有数据

创建线程私有数据
int pthread_key_create(pthread_key_t *key,void (*destr_function)(void *))
注销线程私有数据

  • int pthread_key_delete(pthread_key_t key)
    读写线程私有数据
  • int pthread_setspecific(pthread_key_t key,const void *pointer)
  • void *pthread_getspecific(pthread_key_t key)
    举例
  • 两个线程数据共享(pthread_glob_test.c)
  • 两个线程的私有数据(pthread_key_test.c)

属性创建

基本步骤

  1. ret = pthread_attr_init(&attr);
  2. ret = pthread_attr_get***(&attr,size);ret = pthread_attr_get*(&attr,**size);
  3. ret = pthread_attr_set***(&attr,线程属性); ret = pthread_attr_set***(&attr,线程属性);
  4. ret = pthread_create(&thread,&attr,…);
  5. ret = pthread_attr_destroy(&attr);

属性描述

线程ID

  • 不同进程中创建的线程可能出现ID值相同的情况,这就决定了线程争用范围是在同一进程内竞争。
    举例:pthread_create_id.c

detachstate

  • 线程处于分离状态还是可连接状态。
    举例:pthread_attr_example.c

guardsize

  • 线程守护区大小。

schedparam

  • 线程调度策略相关参数。

schedpolicy

  • 线程调度策略

inheritsched

  • 线程调度策略及参数是从创建线程获得还是从属性对象获得。

stackaddr

  • 线程堆栈基址。

stacksize

  • 线程堆栈大小。

contentionscope

  • 线程争用范围。

processor

  • 线程绑定的特定处理器。

线程的同步

互斥量

mutex互斥锁

  • mutex是一种简单的加锁的方法来控制对共享资源的访问。
  • 在同一时刻只能有一个线程掌握某个互斥上的锁,拥有上锁状态的线程能够对共享资源进行访问。
  • 若其他线程希望上锁一个已经被上了互斥锁的资源,则该线程挂起,直到上锁的线程释放互斥锁为止。

互斥锁的操作步骤
主要包括以下几个:

  • 互斥锁初始化:pthread_mutex_init
  • 互斥锁上锁:pthread_mutex_lock
  • 互斥锁解锁:pthread_mutex_unlock
  • 消除互斥锁:pthread_mutex_destroy

互斥锁初始化

  • #include <pthread.h>
  • int pthread_mutex_init(pthread_mutex_t *mutex,
    const pthread_mutex_attr_t *mutexattr)
    mutex:互斥锁
    mutexattr:通常设置为NULL
    PTHREAD_MUTEX_INITIALIZER:创建快速互斥锁
    PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP:创建递归互斥锁
    PTHREAD_REEORCHECK_MUTEX_INITIALIZER_NP:创建检错互斥锁

互斥锁操作

  • 头文件
    #include <pthread.h>
  • 函数原型
    int pthread_mutex_lock(pthread_mutex_t *mutex)
    int pthread_mutex_unlock(pthread_mutex_t *mutex)
    int pthread_mutex_destroy(pthread_mutex_t *mutex)
  • 说明
    mutex:互斥锁
  • 返回值:成功0,错误-1。
  • 举例
    mutex_example.c

条件变量

出现原因
在这里插入图片描述
初始化条件变量
extern int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr)

销毁条件变量
extern int pthread_cond_destroy(pthread_cond_t *cond)

初始化条件变量举例
pthread_cond_t cv;
Pthread_condattr_t cattr;
int ret;
ret = pthread_cond_init(&cv,NULL);
ret = pthread_cond_init(&cv,&cattr);

通知条件变量
extern int pthread_cond_signal(pthread_cond_t *cond)
extern int pthread_cond_broadcast(pthread_cond_t *cond)

等待条件变量
Extern int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex)

举例
pthread_cond_example.c
thread_cond.c

读写锁

基本原则
如果某线程申请了读锁,其他线程可以再申请读锁,但不能申请写锁;
如果某线程申请了写锁,其他线程不能申请读锁,也不能申请写锁。

初始化读写锁
extern int pthread_rwlock_init(pthread_rwlock_t *rwlock,pthread_rwlockattr_t *attr)

注销读写锁
extern int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)

申请写锁
extern int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)

申请读锁
extern int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)

解锁
extern int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)

举例
pthread_rwlock_example.c

线程与信号

线程在信号操作时有以下特性

  • 每一个线程可以向别的线程发送信号。pthread_kill()函数用来完成这一操作。
  • 每一个线程可以设置自己的信号阻塞集合。pthread_sigmask()函数用来完成这一操作,其类似于进程的sigprocmask()函数。
  • 每个线程可以设置针对某信号处理的方式,但同一进程中对某信号的处理方式只能有一个有效,即最后一次设置的处理方式。
  • 如果别的进程向当前进程中发送一个信号,由哪个线程处理是未知的。

线程信号管理函数——发送信号
extern int pthread_kill(pthread_t threadId,
int signo)
theadId:接收信号的线程
signo:信号,=0,进行错误检查而不发送信号
返回值:成功为0;错误-1
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Caoyang_He/article/details/85234552