这几天学习了Linux 进程,现在学习线程。
一个进程在同一时刻只做一件事情。有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事,每个线程各自处理独立的任务。
一个程序至少个进程,一个进程至少有一个线程。
一、线程
1、创建线程(成功返回0,否则返回错误编号)
原型:
int pthread_create**(pthread_t *restrict **tidp**, const pthread_attr_t *restrict attr, **void *(*start_rtn)(void *)**, void *restrict **arg);
**参数:**
**1.tidp**:指向线程标识符的指针。
**2.attr**:设置线程属性(默认为**NULL**)
**3.(*start_rtn)(void *)**:是一个函数指针,线程运行函数的起始地址。
**4.arg**:运行函数的参数。
**
tidp指向的单元被设置为新创建线程的线程ID。attr参数为线程属性,默认为NULL,新创建的线程是从rtn开始运行的,是一个函数指针,只有一个无类型指针参数arg**,如果传递参数不止一个,那需要定义结构体,把结构体地址作为arg传入。
2、线程等待(成功返回0,否则返回错误编号)
原型:int pthread_join(pthread_t thread, void **rval_ptr);
参数:
thread: 线程标识符,即线程ID,线程名,标识唯一线程。
retval: 用户定义的指针,用来存储被等待线程的返回值。默认为NULL,不关心退出状态
如果线程被取消,由rval_ptr指定的内存单元就置为PTHREAD_CANCELED。以阻塞方式等待thread指定线程结束,参数thread是需要等待的线程ID,rval_ptr是用户定义的指针存储被等待线程的返回值
3、线程退出
原型:int pthread_exit(void *rval_ptr);
参数:是个空类型的指针保存的是线程退出以后的返回值,进程中的其他线程可以通过调用pthread_join函数访问到这个指针,不需要返回也可以直接NULL
4、线程的ID
原型:pthread_t pthread_self(void);
// 返回:调用线程的ID
5.线程脱离
原型:int pthread_detach(pthread_t tid);
参数:tid:线程标识符
pthread_join()函数的替代函数,可回收创建时detachstate属性设置为PTHREAD_CREATE_JOINABLE的线程的存储空间。
该函数不会阻塞父线程。pthread_join()函数用于只是应用程序在线程tid终止时回收其存储空间。如果tid尚未终止,pthread_detach() 不会终止该线程。当然**pthread_detach(pthread_self())**也是可以的。
6.线程ID的比较(若相等则返回非0值,否则返回0)
原型:int pthread_equal(pthread_t tid1, pthread_t tid2);
对于线程ID比较,为了可移植操作,我们不能简单地把线程ID当作整数来处理,因为不同系统对线程ID的定义可能不一样。我们应该要用上边的函数。
代码实现一下创建一个线程,然后打印改线程的ID,同时main函数也打印一下线程ID。
代码:
运行结果:
注意文明在编译的时候要加上-lpthread
二、与互斥锁相关API
互斥量就是一个锁。有加锁销毁锁操作。加锁跟解锁包含起来的我们叫共享资源。当我们想运行到某个线程中的内容时候,就想单单让该线程的内容执行完再执行其他线程,就要用到互斥锁。
1.创建互斥锁(成功返回0,否则返回错误编号)
原型:int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
attr:NULL为默认属性。
pthread_mutex_init()函数是以动态方式创建互斥锁的,参数attr指定了新建互斥锁的属性。如果参数attr为空(NULL),则使用默认的互斥锁属性,默认属性为快速互斥锁 。
2.销毁互斥锁(成功返回0,否则返回错误编号)
原型:int pthread_mutex_destroy(pthread_mutex_t *mutex); //mutex 指向要销毁的互斥锁的指针
3.加锁(成功返回0,否则返回错误编号)
原型:int pthread_mutex_lock(pthread_mutex_t *mutex);
4.解锁(成功返回0,否则返回错误编号)
int pthread_mutex_unlock(pthread_mutex_t *mutex);
如果线程不希望被阻塞,它可以使用pthread_mutex_trylock尝试对互斥量进行加锁。如果调用pthread_mutex_trylock时互斥量处于未锁住状态,那么pthread_mutex_trylock将锁住互斥量,不会出现阻塞并返回0,否则pthread_mutex_trylock就会失败,不能锁住互斥量,而返回EBUSY。
我们代码用一下互斥锁这么API。
让一个线程每隔1秒打印线程ID(一共打印5次),让一个线程每隔一秒打印ID(一共打印3次)。我们用互斥锁的作用就是能让一个线程先执行完再执行另一个线程。这就是互斥锁的作用
代码:
运行结果:
三、与条件变量相关API
上面说了互斥锁,其实只能说运行到某个线程,能把这么线程的内容运行完再运行其他线程,但不能控制到我想某个线程先运行,某个线程先等待。那么下面有几个API就能控制线程谁先运行,谁等待。
1.创建条件/初始化条件(成功返回0,否则返回错误编号)
原型:int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
参数:
cond:是一个指向结构pthread_cond_t的指针
attr:默认NULL
除非需要创建一个非默认属性的条件变量,否则pthread_cont_init函数的attr参数可以设置为NULL。
静态创建:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER
2.销毁条件(成功返回0,否则返回错误编号)
原型:int pthread_cond_destroy(pthread_cond_t cond);
cond:指向pthread_cond_t结构的指针。
3. 等待(成功返回0,否则返回错误编号)
原型:int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
作用:让线程阻塞
4.触发(成功返回0,否则返回错误编号)
原型:int pthread_cond_signal(pthread_cond_t cond);
作用:唤醒处于阻塞的线程。
我们用代码使用一下上面的API,比如让一个线程从0不断的+1,等加到3,我们让另一个线程打印并且变为0。不断循环,循环3次。这样就可以主动控制某个线程先执行了。
代码:
运行结果: