做网站模板和服务器是一样的吗,网页登不了wordpress,想创业去哪里找项目,金螳螂装饰公司国内排名一、互斥锁
临界资源概念#xff1a;
不能同时访问的资源#xff0c;比如写文件#xff0c;只能由一个线程写#xff0c;同时写会写乱。
比如外设打印机#xff0c;打印的时候只能由一个程序使用。
外设基本上都是不能共享的资源。
生活中比如卫生间#xff0c;同一… 一、互斥锁
临界资源概念
不能同时访问的资源比如写文件只能由一个线程写同时写会写乱。
比如外设打印机打印的时候只能由一个程序使用。
外设基本上都是不能共享的资源。
生活中比如卫生间同一时间只能由一个人使用。
必要性 临界资源不可以共享
两种方法创建互斥锁静态方式和动态方式
动态方式
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
其中mutexattr用于指定互斥锁属性如果为NULL则使用缺省属性。
静态方式
pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER;锁的销毁
int pthread_mutex_destroy(pthread_mutex_t *mutex)
在Linux中互斥锁并不占用任何资源
因此LinuxThreads中的 pthread_mutex_destroy()除了检查锁状态以外锁定状态则返回EBUSY没有其他动作。互斥锁的使用
int pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_unlock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t *mutex)vim 设置代码全文格式化ggG查看线程 没有加互斥锁
#includepthread.h
#includestdio.h
#includestdlib.h
#includestring.h
#includeunistd.hFILE* fp;void* testattr(void* arg)
{pthread_detach(pthread_self());printf(This is testattr pthread\n);char str[] You read testattr thread\n;char c;int i 0;while(1){while(istrlen(str)){c str[i];int ret 0;ret fputc(c,fp);i;usleep(1);}i 0;usleep(1);}pthread_exit(testattr exit);
}void* testattr2(void* arg)
{pthread_detach(pthread_self());printf(This is testattr2 pthread\n);char str[] I write testattr2 line\n;char c;int i 0;while(1){while(istrlen(str)){c str[i];int ret 0;ret fputc(c,fp);i;usleep(1);}i 0;usleep(1);}pthread_exit(testattr2 exit);
}int main()
{pthread_t pthread;pthread_t pthread2;int i 0;void* retv;fp fopen(1.txt,a);if(fp NULL){perror(fp);exit(-1);}pthread_create(pthread,NULL,testattr,NULL);pthread_create(pthread2,NULL,testattr2,NULL);while(1){sleep(1);}fclose(fp);return 0;
}1.txt中的值 加上互斥锁
#include pthread.h
int pthread_mutex_lock(pthread_mutex_t *mutex);
mutex 参数是一个指向互斥锁对象的指针该锁对象必须是一个已经初始化的互斥锁。
pthread_mutex_lock 函数尝试对互斥锁进行加锁。如果互斥锁当前没有被锁住那么调用将成功该线程将对互斥锁进行加锁并立即返回。如果互斥锁当前被其他线程锁住那么调用将被阻塞直到互斥锁被释放。
在加锁之后线程负责确保在对共享资源的访问完成后调用 pthread_mutex_unlock 函数进行解锁以允许其他线程获得对互斥锁的访问。
pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER;
是一种静态初始化互斥锁的方式。
在使用互斥锁之前必须对其进行初始化。
PTHREAD_MUTEX_INITIALIZER 是一个宏它在编译时为互斥锁对象提供了默认的初始值。
#includepthread.h
#includestdio.h
#includestdlib.h
#includestring.h
#includeunistd.hFILE* fp;
pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER;
void* testattr(void* arg)
{pthread_detach(pthread_self());printf(This is testattr pthread\n);char str[] You read testattr thread\n;char c;int i 0;while(1){pthread_mutex_lock(mutex);while(istrlen(str)){c str[i];int ret 0;ret fputc(c,fp);i;usleep(1);}pthread_mutex_unlock(mutex);i 0;usleep(1);}pthread_exit(testattr exit);
}void* testattr2(void* arg)
{pthread_detach(pthread_self());printf(This is testattr2 pthread\n);char str[] I write testattr2 line\n;char c;int i 0;while(1){pthread_mutex_lock(mutex);while(istrlen(str)){c str[i];int ret 0;ret fputc(c,fp);i;usleep(1);}pthread_mutex_unlock(mutex);i 0;usleep(1);}pthread_exit(testattr2 exit);
}int main()
{pthread_t pthread;pthread_t pthread2;int i 0;void* retv;fp fopen(1.txt,a);if(fp NULL){perror(fp);exit(-1);}pthread_create(pthread,NULL,testattr,NULL);pthread_create(pthread2,NULL,testattr2,NULL);while(1){sleep(1);}fclose(fp);return 0;
}1.txt中的值 动态方式创建互斥锁 int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr); 其中mutexattr用于指定互斥锁属性如果为NULL则使用缺省属性。
二、读写锁
必要性提高线程执行效率
特性 写者写者使用写锁如果当前没有读者也没有其他写者写者立即获得写锁否则写者将等待直到没有读者和写者。 读者读者使用读锁如果当前没有写者读者立即获得读锁否则读者等待直到没有写者。
注意 同一时刻只有一个线程可以获得写锁同一时刻可以有多个线程获得读锁。 读写锁处于写锁状态时所有试图对读写锁加锁的线程不管是读者试图加读锁还是写者试图加写锁都会被阻塞。 读写锁处于读锁状态时有写者试图加写锁时之后的其他线程的读锁请求会被阻塞以避免写者长时间的不写锁
初始化一个读写锁 pthread_rwlock_init读锁定读写锁 pthread_rwlock_rdlock非阻塞读锁定 pthread_rwlock_tryrdlock写锁定读写锁 pthread_rwlock_wrlock非阻塞写锁定 pthread_rwlock_trywrlock解锁读写锁 pthread_rwlock_unlock释放读写锁 pthread_rwlock_destroy
int pthread_detach(pthread_t thread); 成功0失败错误号作用从状态上实现线程分离注意不是指该线程独自占用地址空间。线程分离状态指定该状态线程主动与主控线程断开关系。线程结束后不会产生僵尸线程其退出状态不由其他线程获取而直接自己自动释放自己清理掉PCB的残留资源。网络、多线程服务器常用。 示例代码
#include pthread.h
#include stdio.h
#include unistd.h
#include string.hpthread_rwlock_t rwlock;FILE *fp;
void * read_func(void *arg){pthread_detach(pthread_self());printf(read thread\n);char buf[32]{0};while(1){pthread_rwlock_rdlock(rwlock);while(fgets(buf,32,fp)!NULL){printf(%d,rd%s\n,(int)arg,buf);usleep(1000);}pthread_rwlock_unlock(rwlock);sleep(1);}}void *func2(void *arg){pthread_detach(pthread_self());printf(This func2 thread\n);char str[]I write func2 line\n;char c;int i0;while(1){pthread_rwlock_wrlock(rwlock);while(istrlen(str)){c str[i];fputc(c,fp);usleep(1);i;}pthread_rwlock_unlock(rwlock);i0;usleep(1);}pthread_exit(func2 exit);}void *func(void *arg){pthread_detach(pthread_self());printf(This is func1 thread\n);char str[]You read func1 thread\n;char c;int i0;while(1){pthread_rwlock_wrlock(rwlock);while(istrlen(str)){c str[i];fputc(c,fp);i;usleep(1);}pthread_rwlock_unlock(rwlock);i0;usleep(1);}pthread_exit(func1 exit);
}int main(){pthread_t tid1,tid2,tid3,tid4;void *retv;int i;fp fopen(1.txt,a);if(fpNULL){perror(fopen);return 0;}pthread_rwlock_init(rwlock,NULL);pthread_create(tid1,NULL,read_func,1);pthread_create(tid2,NULL,read_func,2);
sleep(1);
//读写是抢占不是按顺序
//有时候一直在调用读写没排上号
//谁抢上是谁,所以加上sleep(1)pthread_create(tid3,NULL,func,NULL);pthread_create(tid4,NULL,func2,NULL);while(1){ sleep(1);} } 文件和锁的初始化 该程序以附加模式a打开名为1.txt的文件使用fopen。如果文件不存在它尝试创建它。使用pthread_rwlock_init初始化读写锁。 线程函数 read_func该函数负责从文件中读取。它不断从文件中读取行并将它们打印到控制台。它使用读锁以确保它可以与其他读取器并发读取但在读取正在进行时不能写入。testattr和testattr1这些函数不断将预定义的字符串写入文件。它们使用写锁以确保只有其中一个可以同时写入文件。 主函数 它创建五个线程两个用于读取pthread2和pthread3三个用于写入pthreadpthread1和主线程。主线程进入一个无限循环while(1)以使程序无限期运行。 线程分离 使用pthread_detach(pthread_self())分离每个线程以在线程退出时自动回收资源。 线程同步 读写操作由读写锁pthread_rwlock_rdlock和pthread_rwlock_wrlock保护以确保正确的同步并避免数据损坏。 文件操作 文件操作使用标准文件I/O函数fgets用于读取fputc用于写入。 睡眠和延迟 使用usleep和sleep引入操作之间的延迟以更好地展示并发行为。 主函数中的无限循环 通过带有sleep(1)延迟的无限循环保持主线程处于活动状态。
那读写的顺序是什么 在这个程序中有两个读线程 (read_func) 和两个写线程 (testattr 和 testattr1)以及主线程。读线程和写线程是同时运行的并且使用了读写锁 (pthread_rwlock_t rwlock) 来确保对文件的安全访问。 读线程 (read_func) 在一个无限循环中通过 pthread_rwlock_rdlock 获取读锁然后通过 fgets 从文件中读取内容。读操作完成后通过 pthread_rwlock_unlock 释放读锁。这表示读线程首先获取读锁然后读取文件内容。 写线程 (testattr 和 testattr1) 在一个无限循环中通过 pthread_rwlock_wrlock 获取写锁然后通过 fputc 向文件写入内容。写操作完成后通过 pthread_rwlock_unlock 释放写锁。这表示写线程首先获取写锁然后写入文件内容。 总体而言程序中的读线程和写线程是同时运行的但是通过使用读写锁可以确保对文件的安全访问防止读和写操作之间的冲突。
注意
读线程 (read_func) 在读取文件时获取读锁 (pthread_rwlock_rdlock)
这时其他读线程也可以同时获取读锁。
这允许多个读线程同时读取文件内容因为读操作不会互斥。
在使用读写锁时写线程获得写锁时会阻塞其他写线程和读线程
以确保在写入文件时不会同时有其他线程读或写。在这个程序中当一个线程写线程获得写锁时
其他线程包括读线程和其他写线程都会被阻塞直到写线程释放写锁。所以写线程会独占地访问文件直到它完成写入并释放写锁。
这种方式确保了写的原子性防止多个写线程之间和读线程之间的竞争条件。
为什么需要读锁
虽然读操作本身不会改变数据但如果在读的同时有其他线程在写就可能读到不一致或不准确的数据。
因此在多线程环境下为了确保数据的一致性读操作也需要进行同步而这就是使用读写锁的原因。
上述的程序中pthread_rwlock_rdlock 用于获取读锁而 pthread_rwlock_unlock 用于释放读锁。
这样做的目的是通过获取读锁确保在读取文件内容时不会被写线程中断避免了读线程和写线程之间的竞态条件。释放读锁以允许其他读线程或写线程访问文件。使用读写锁可以实现多个线程同时读取文件但在写线程写入时阻塞读线程以保证对文件的安全访问。
这种同步机制确保了对共享资源的正确和一致的访问。
为什么此代码只需要一个读的回调函数
在这个特定的程序中可能只需要一个读回调函数。
读回调函数负责获取读锁读取文件内容然后释放读锁以确保在读的过程中其他线程不会写入文件。
由于读操作本身不修改数据多个读线程可以并发地执行。写操作则需要更谨慎地处理因为写线程在写入时需要独占访问避免与其他写线程或读线程发生冲突。
因此写回调函数需要获取写锁执行写操作然后释放写锁。
这里为什么需要usleep(1000)?
usleep(1000); 的目的是让读线程休眠一毫秒1000微秒。
这样的操作通常是为了减缓循环的执行速度以避免过于频繁地执行文件读取操作。
usleep 函数用于在指定的微秒数内挂起当前线程的执行。