线程同步
1.互斥锁
同一个时刻只允许一个线程执行一段关键代码,防止发生读写错乱。
锁的初始化(使用互斥锁之前必须先初始化)
1.将宏(PTHREAD_MUTEX_INITIALIZER)赋给锁(pthread_mutex_t mlock)
2.使用 pthread_mutex_init 函数
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
参数restrict attr表示互斥锁的属性,NULL 为默认属性 。
加锁与解锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
说明:1.加锁时,如果锁已经被加了,加锁线程就会阻塞等待,直到解锁
2.pthread_mutex_lock 返回说明加锁成功 ,不返回说明阻塞等待 。pthread_mutex_trylock返回0 说明没有加锁,返回其他值说明已经被加锁(类比于文件锁F_GETLK)
int pthread_mutex_unlock(pthread_mutex_t *mutex);
解锁时需要满足的条件:1.互斥锁处于加锁的状态
2.加锁的线程必须是给他上锁的线程。(一句话:解铃还需系铃人!! )
锁的清除
int pthread_mutex_destroy(pthread_mutex_t *mutex); //释放互斥锁占用的资源
必须保证锁被解开 ,否则返回EBUSY 。成功返回0
具体实现过程介绍:一个线程等待条件变量被设置为真,另一个线程在使用完资源后设置条件为真 。互斥锁保护条件变量
1.条件变量的初始化
1.将宏(PTHREAD_COND_INITIALIZER)赋给条件变量(pthread_cond_t mcond)
2.使用 pthread_cond_init 函数
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
参数restrict attr表示互斥锁的属性,%99 的情况下用的是NULL
2.等待条件变量成立
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
说明:pthread_cond_wait 释放互斥锁,等待条件变量被设置为真,pthread_cond_timedwait 规定了等待时间time.越时返回ETIMEOUT ,结束等待。
3.激活条件变量
#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
说明:pthread_cond_signal 激活一个等待条件变量为真的线程。 pthread_cond_broadcast 激活所有等待线程
4.清除条件变量
int pthread_cond_destroy(pthread_cond_t *cond);
说明:没有线程等待该条件变量成立时清除,否则返回EBUSY
小示例:
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
pthread_mutex_t mutex ;
pthread_cond_t cond ;
void *thread1( void *arg )
{
pthread_cleanup_push(pthread_mutex_unlock ,&mutex);
while (1 )
{
printf ("thread1 is running !! \n" );
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond ,&mutex);
printf ("thread 1 is apply the conditionn !! \n" );
pthread_mutex_unlock(&mutex);
sleep(1 );
}
pthread_cleanup_pop(0 );
}
void *thread2( void *arg )
{
while (1 )
{
printf ("thread2 is running !! \n" );
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond ,&mutex);
printf ("thread2 is apply the conditionn !! \n" );
pthread_mutex_unlock(&mutex);
sleep(1 );
}
}
int main(void )
{
pthread_t tid1 ,tid2 ;
printf ("520520505201505205020502220500\n" );
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
pthread_create(&tid1 ,NULL,(void *)thread1 ,NULL);
pthread_create(&tid2 ,NULL,(void *)thread2 ,NULL);
do
{
pthread_cond_signal(&cond);
}while (1 );
sleep(50 );
pthread_exit(0 );
}
执行结果:
说明:两个线程被启动,等待同一个条件变量,main 函数中循环激活条件变量,使得两个线程同步运行
3.异步信号
信号与任何线程异步,也就是说信号到达线程的时间是不定的 。如果有多个线程可以接受信号,则只有一个被选中。如果有许多的信号被传送,则分配给每一个线程处理,不会重复。如果所有的线程都屏蔽该信号,信号就会挂起,直到有信号解除屏蔽。
#include <signal.h>
int pthread_kill(pthread_t thread, int sig);
int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
int sigwait(const sigset_t *set, int *sig);
说明:pthread_kill 向特定的线程发送信号。pthread_sigmask设置线程信号屏蔽码,但对不允许屏蔽的Cancel和不允许响应的Restart 进行了保护。sigwait阻塞线程,等待set 中指定的信号之一到达 ,存放在sig 中
出错处理
头文件errno.h 中定义了变量errno 存储了错误发生时的错误码 ,程序刚开始时为0(其实errno相当于全局变量了)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<errno.h>
#include<string.h>
FILE* open_file(char *filename)
{
FILE *stream ;
errno = 0 ;
stream=fopen(filename ,"rw+" );
if (!stream)
{
printf ("can not open the %s : error == %d \n" ,filename ,errno);
exit (-1 );
}
else return stream ;
}
int main(void )
{
char *filename="test" ;
open_file(filename);
return 0 ;
}
执行结果:
错误码
常用错误码查询
#ifndef _I386_ERRNO_H
#define _I386_ERRNO_H
#define EPERM 1 / *不允许操作* /
#define ENOENT 2 / *没有这样的文件或目录* /
#define ESRCH 3 / *没有这样的过程* /
#define EINTR 4 / *中断的系统调用* /
#define EIO 5 / * I / O错误* /
#define ENXIO 6 / *没有这样的设备或地址* /
#define E2BIG 7 / * Arg列表太长* /
#define ENOEXEC 8 / * Exec格式错误* /
#define EBADF 9 / *不良文件号* /
#define ECHILD 10 / *没有子进程* /
#define EAGAIN 11 / *再试一次* /
#define ENOMEM 12 / *内存不足* /
#define EACCES 13 / *权限被拒绝* /
#define EFAULT 14 / *错误地址* /
#define ENOTBLK 15 / *阻止设备* /
#define EBUSY 16 / *设备或资源繁忙* /
#define EEXIST 17 / *文件存在* /
#define EXDEV 18 / *跨设备链接* /
#define ENODEV 19 / *没有这样的设备* /
#define ENOTDIR 20 / *不是目录* /
#define EISDIR 21 / *是一个目录* /
#define EINVAL 22 / *参数无效* /
#define ENFILE 23 / *文件表溢出* /
#define EMFILE 24 / *打开的文件太多* /
#define ENOTTY 25 / *不是打字机* /
#define ETXTBSY 26 / *文本文件忙* /
#define EFBIG 27 / *文件太大* /
#define ENOSPC 28 / *设备上没有剩余空间* /
#define ESPIPE 29 / *非法寻求* /
#define EROFS 30 / *只读文件系统* /
#define EMLINK 31 / *链接太多* /
#define EPIPE 32 / *破碎管* /
#define EDOM 33 / *从func * /
#define ERANGE 34 / *数学结果不可代表* /
#define EDEADLK 35 / *资源死锁将发生* /
#define ENAMETOOLONG 36 / *文件名太长* /
#define ENOLCK 37 / *无记录锁* /
#define ENOSYS 38 / *功能未实现* /
#define ENOTEMPTY 39 / *目录不为空* /
#define ELOOP 40 / *遇到太多的符号链接* /
#define EWOULDBLOCK EAGAIN / *操作将阻止* /
#define ENOMSG 42 / *没有所需类型的消息* /
#define EIDRM 43 / *标识符已删除* /
#define ECHRNG 44 / *频道号超出范围* /
#define EL2NSYNC 45 / * 2级不同步* /
#define EL3HLT 46 / * 3级停止* /
#define EL3RST 47 / * 3级复位* /
#define ELNRNG 48 / *链接号超出范围* /
#define EUNATCH 49 / *协议驱动程序未附加* /
#define ENOCSI 50 / *无CSI结构* /
#define EL2HLT 51 / * 2级暂停* /
#define EBADE 52 / *无效交换* /
#define EBADR 53 / *无效的请求描述符* /
#define EXFULL 54 / * Exchange full * /
#define ENOANO 55 / *无阳极* /
#define EBADRQC 56 / *请求代码无效* /
#define EBADSLT 57 / *无效插槽* /
#define EDEADLOCK EDEADLK
#define EBFONT 59 / *坏字体文件格式* /
#define ENOSTR 60 / *设备不是流* /
#define ENODATA 61 / *无数据资料* /
#define ETIME 62 / *计时器已过期* /
#define ENOSR 63 / *从流资源* /
#define ENONET 64 / *机器不在网络上* /
#define ENOPKG 65 / *软件包未安装* /
#define EREMOTE 66 / * Object is remote * /
#define ENOLINK 67 / * Link已被切断* /
#define EADV 68 / *广告错误* /
#define ESRMNT 69 / * Srmount错误* /
#define ECOMM 70 / *发送通信错误* /
#define EPROTO 71 / *协议错误* /
#define EMULTIHOP 72 / * Multihop尝试* /
#define EDOTDOT 73 / * RFS具体错误* /
#define EBADMSG 74 / *不是数据信息* /
#define EOVERFLOW 75 / *值对于定义的数据类型来说太大* /
#define ENOTUNIQ 76 / *网络名称不唯一* /
#define EBADFD 77 / *文件描述符处于坏状态* /
#define EREMCHG 78 / *远程地址更改* /
#define ELIBACC 79 / *无法访问所需的共享库* /
#define ELIBBAD 80 / *访问损坏的共享库* /
#define ELIBSCN 81 / * .lib部分在a.out损坏* /
#define ELIBMAX 82 / *尝试链接到太多的共享库* /
#define ELIBEXEC 83 / *无法直接执行共享库* /
#define EILSEQ 84 / *非法字节序列* /
#define ERESTART 85 / *中断的系统调用应重新启动* /
#define ESTRPIPE 86 / *流管道错误* /
#define EUSERS 87 / *用户太多* /
#define ENOTSOCK 88 / *套接字操作在非socket * /
#define EDESTADDRREQ 89 / *需要目的地址* /
#define EMSGSIZE 90 / *留言太久* /
#define EPROTOTYPE 91 / *协议错误类型为套接字* /
#define ENOPROTOOPT 92 / *协议不可用* /
#define EPROTONOSUPPORT 93 / *不支持协议* /
#define ESOCKTNOSUPPORT 94 / *不支持套接字* /
#define EOPNOTSUPP 95 / *传输端点不支持的操作* /
#define EPFNOSUPPORT 96 / *不支持协议族*
#define EAFNOSUPPORT 97 / *协议不支持的地址族*
#define EADDRINUSE 98 / *地址已在使用* /
#define EADDRNOTAVAIL 99 / *无法分配请求的地址* /
#define ENETDOWN 100 / *网络已关闭* /
#define ENETUNREACH 101 / *网络无法访问* /
#define ENETRESET 102 / *网络由于重置而丢弃连接* /
#define ECONNABORTED 103 / *软件导致连接中止* /
#define ECONNRESET 104 / *由对等体重新连接*/
提示错误信息
#include<stdio.h>
void perror(const char *s);
#include<string.h>
char *strerror(int errnum);
说明: 1. strerror 根据参数errnum获得描述错误信息的字符串 . 2. perror 打印错误信息到stderr ,如果为空,打印错误信息,如果不为空,先打印参数s ,然后添加冒号和空格 ,最后是错误信息