2016-08-13 21:33:15 daiyudong2020 阅读数 1645


基本概念:

读写锁也叫做共享互斥锁。

当读写锁是写加锁状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞。当读写锁在读加锁状态,所有试图以读模式对它进行加锁的线程都可以得到访问权。

与互斥量相比,读写锁在使用之前必须初始化,在释放它们底层的内存之前必须销毁。


一、锁的初始化与销毁

PTHREAD_RWLOCK_DESTROY(P)  POSIX Programmer's Manual PTHREAD_RWLOCK_DESTROY(P)

NAME
       pthread_rwlock_destroy,  pthread_rwlock_init - destroy and initialize a
       read-write lock object

SYNOPSIS
       #include <pthread.h>

       int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
       int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
              const pthread_rwlockattr_t *restrict attr);
两个函数的返回值:若成功,返回0;否则,返回错误编号


二、读锁

PTHREAD_RWLOCK_RDLOCK(P)   POSIX Programmer's Manual  PTHREAD_RWLOCK_RDLOCK(P)

NAME
       pthread_rwlock_rdlock,  pthread_rwlock_tryrdlock  -  lock  a read-write
       lock object for reading

SYNOPSIS
       #include <pthread.h>

       int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
       int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
两个函数的返回值:若成功,返回0;否则,返回错误编号
pthread_rwlock_tryrdlock函数可以获取锁时,返回0.否则,返回错误EBUSY


三、写锁

PTHREAD_RWLOCK_TRYWRLOCK(P)POSIX Programmer's ManuaPTHREAD_RWLOCK_TRYWRLOCK(P)

NAME
       pthread_rwlock_trywrlock,  pthread_rwlock_wrlock  -  lock  a read-write
       lock object for writing

SYNOPSIS
       #include <pthread.h>

       int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
       int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
两个函数的返回值:若成功,返回0;否则,返回错误编号

pthread_rwlock_trywrlock函数可以获取锁时,返回0.否则,返回错误EBUSY


四、带有超时的读写锁

PTHREAD_RWLOCK_TIMEDWRLOCK(POSIX Programmer's ManPTHREAD_RWLOCK_TIMEDWRLOCK(P)

NAME
       pthread_rwlock_timedrdlock - lock a read-write lock for reading
       pthread_rwlock_timedwrlock - lock a read-write lock for writing

SYNOPSIS
       #include <pthread.h>
       #include <time.h>

       int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock,
              const struct timespec *restrict abs_timeout);
       int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock,
              const struct timespec *restrict abs_timeout);
两个函数的返回值:若成功,返回0;否则,返回错误编号
如果它们不能获取锁,那么超时到期时,这两个函数将返回ETIMEDOUT


五、解锁

PTHREAD_RWLOCK_UNLOCK(P)   POSIX Programmer's Manual  PTHREAD_RWLOCK_UNLOCK(P)

NAME
       pthread_rwlock_unlock - unlock a read-write lock object

SYNOPSIS
       #include <pthread.h>

       int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
返回值:若成功,返回0;否则,返回错误编号

读写锁属性:

读写锁支持的唯一属性是进程共享属性。它与互斥量的进程共享属性是相同的,这里不展开讨论。


例子:gcc pthread_rwlock.c -pthread

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>

static int num = 0;
static int count = 100000;
static pthread_rwlock_t rwlock;

void Perror(const char *s)
{
    perror(s);
    exit(EXIT_FAILURE);
}

void* fun2(void *arg)
{
    pthread_t thread_id = pthread_self();
    printf("the thread2 id is %ld\n", (long)thread_id);
    int i = 1;
    for (; i<=count; ++i) {
        pthread_rwlock_wrlock(&rwlock);
        num += 1;
        pthread_rwlock_unlock(&rwlock);
    }
}

void* fun3(void *arg)
{
    pthread_t thread_id = pthread_self();
    printf("the thread3 id is %ld\n", (long)thread_id);
    int i = 1;
    for (; i<=count; ++i) {
        pthread_rwlock_wrlock(&rwlock);
        num += 1;
        pthread_rwlock_unlock(&rwlock);
    }
}

int main()
{
    int err;
    pthread_t thread1;
    pthread_t thread2;
    pthread_t thread3;

    // init
    pthread_rwlock_init(&rwlock, NULL);

    thread1 = pthread_self();
    printf("the thread1 id is %ld\n", (long)thread1);

    // Create thread
    err = pthread_create(&thread2, NULL, fun2, NULL);
    if (err != 0) {
        Perror("can't create thread2\n");
    }
    err = pthread_create(&thread3, NULL, fun3, NULL);
    if (err != 0) {
        Perror("can't create thread3\n");
    }

    // detach thread
    err = pthread_detach(thread2);
    if (err != 0) {
        Perror("can't detach thread2\n");
    }
    err = pthread_detach(thread3);
    if (err != 0) {
        Perror("can't detach thread3\n");
    }

    int i = 1;
    for (; i<=count; ++i) {
        pthread_rwlock_rdlock(&rwlock);
        int temp = num;
        pthread_rwlock_unlock(&rwlock);
    }

    sleep(10);
    printf("The num is %d\n", num);
    
    pthread_rwlock_destroy(&rwlock);
    return 0;
}


参考:《unix环境高级编程》·第三版

End;

2019-07-31 23:51:10 qq_38632611 阅读数 14

思路: linux多线程时,数据空间为公共,一个线程去添加数据,一个线程去修改数据,这个时候需要加入互斥锁,俩个线程如果同时去处理这个数据空间,数据会出错,除了线程锁之外,学习了一个读写锁
详细函数说明: https://www.cnblogs.com/x_wukong/p/5671537.html

核心函数:
初始化读写锁 pthread_rwlock_init
写入读写锁中的锁 pthread_rwlock_wrlock(阻塞)
解除锁定读写锁 pthread_rwlock_unlock

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <unistd.h>

pthread_rwlock_t  rwlock = PTHREAD_RWLOCK_INITIALIZER;

char tmp_buf[100] = { 0 };

void test()
{
	time_t now_time = 0;
	while (1)
	{
		pthread_rwlock_wrlock(&rwlock);
		now_time = time(NULL);
		snprintf(tmp_buf, sizeof(tmp_buf) - 1, "%ld", now_time);
		printf("test : %s\n", tmp_buf);
		pthread_rwlock_unlock(&rwlock);
		sleep(2);
	}
}

int main()
{
	int ret = pthread_rwlock_init(&rwlock, NULL);
	pthread_t  fp;
	
	if (ret != 0)
	{
		printf("creat err!\n");
	}

	if ((pthread_create(&fp, NULL, (void *)&test, NULL)) == -1)
    {
	       printf("create error!\n");
		      return 1;
    }

	while (1)
	{
		pthread_rwlock_wrlock(&rwlock);
		snprintf(tmp_buf, sizeof(tmp_buf) - 1, "test");
		sleep(5);
		printf("main : %s\n", tmp_buf);
		pthread_rwlock_unlock(&rwlock);
		sleep(1);
	}
}
gcc test_time.c -lpthread -o hello

结果:

不加锁

./hello 
test : 1564587467
test : 1564587469
test : 1564587471
main : 1564587471
test : 1564587473
test : 1564587475
test : 1564587477
main : 1564587477
test : 1564587479
test : 1564587481

加锁

./hello 
main : test
test : 1564587516
main : test
test : 1564587522
main : test
test : 1564587528
2016-04-07 20:09:00 lotluck 阅读数 2088

        这次当我给面试官提及到读写锁的时候,详细问了我的读写锁,我知道怎么回事,用法,但是具体没实践过,听过的还是不要给面试官说了,不然又给自己挖坑。

   下面这个程序就是读写锁的程序,分别有两个读者,两个写着,当写着输入end的时候并且读者读到程序运行结束,里面的读写锁是动态初始化,最后用pthread_join(),主要主线程等待子线程运行完她才可以结束。对于现在锁,我才感觉到都是有套路的,都被玩坏了。初始化,处理的,最后是销毁的。此外,我还遇到的一个问题是当时忘记在写线程忘记休眠sleep,结果一直让我写,写,写,那么她就一直拿着cpu写,就算输入end也就结束不了,因为读线程根本执行不到。


#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <bits/pthreadtypes.h>


#define  WORK_SIZE  1024

static  pthread_rwlock_t  rwlock;

char work_area[WORK_SIZE];
int time_to_exit;

void *thread_to_read_o(void *arg);
void *thread_to_read_t(void *arg);
void *thread_to_write_o(void *arg);
void *thread_to_write_t(void *arg);


void *thread_to_read_o(void *arg)
{
    printf("thread read one try to get lock\n");

    pthread_rwlock_rdlock(&rwlock);
    while(strncmp("end",work_area,3) != 0){
        printf("this is thread read one\n");
        printf("the characters is %s\n",work_area);
        pthread_rwlock_unlock(&rwlock);
        sleep(2);
        pthread_rwlock_rdlock(&rwlock);
        while(work_area[0] == '\0'){
           pthread_rwlock_unlock(&rwlock);
           sleep(2);
           pthread_rwlock_rdlock(&rwlock);
        }
    }
    pthread_rwlock_unlock(&rwlock);
    time_to_exit = 1;
    pthread_exit(0);
}

void *thread_to_read_t(void *arg)
{
    printf("thread read one try to get lock\n");

    pthread_rwlock_rdlock(&rwlock);
    while(strncmp("end",work_area,3) != 0){
        printf("this is thread read two\n");
        printf("the characters is %s\n",work_area);
        pthread_rwlock_unlock(&rwlock);
        sleep(5);
        pthread_rwlock_rdlock(&rwlock);
        while(work_area[0] == '\0'){
             pthread_rwlock_unlock(&rwlock);
             sleep(5);
             pthread_rwlock_rdlock(&rwlock);
        }
    }
    pthread_rwlock_unlock(&rwlock);
    time_to_exit = 1;
    pthread_exit(0);
}
void *thread_to_write_o(void *arg)
{
    printf("this is write thread one try to get lock\n");
    while(!time_to_exit){
           pthread_rwlock_wrlock(&rwlock);
           printf("this is write thread one\n,input some text,enter 'end'  to finish\n");
           fgets(work_area,WORK_SIZE,stdin);
           pthread_rwlock_unlock(&rwlock);
           sleep(15); // forget sleep,so write always
    }

    pthread_rwlock_unlock(&rwlock);
    pthread_exit(0);
}
void *thread_to_write_t(void *arg)
{
    sleep(10);
    while(!time_to_exit){
       pthread_rwlock_wrlock(&rwlock);
       printf("this is write thread two\n input some text,enter 'end' to finish\n");
       fgets(work_area,WORK_SIZE,stdin);
       pthread_rwlock_unlock(&rwlock);
       sleep(20);
    }
    pthread_rwlock_unlock(&rwlock);
    pthread_exit(0);
}




int main(int argc,char *argv[])
{
    int retval;
    pthread_t a_thread,b_thread,c_thread,d_thread;
    void *thread_result;

    retval = pthread_rwlock_init(&rwlock,NULL);
    if(retval != 0){
          //  error
          exit(1);
    }

    retval = pthread_create(&a_thread,NULL,thread_to_read_o,NULL);
    if(retval != 0){
          //  error
          exit(1);
    }

    retval = pthread_create(&b_thread,NULL,thread_to_read_t,NULL);
    if(retval != 0){
          //  error
          exit(1);
    }

    retval = pthread_create(&c_thread,NULL,thread_to_write_o,NULL);
    if(retval != 0){
          //  error
          exit(1);
    }
    retval = pthread_create(&d_thread,NULL,thread_to_write_t,NULL);
    if(retval != 0){
          //  error
          exit(1);
    }

    // suspend main thread
    pthread_join(a_thread,NULL);

    pthread_join(b_thread,NULL);

    pthread_join(c_thread,NULL);

    pthread_join(d_thread,NULL);


    // destroy rwlock
    pthread_rwlock_destroy(&rwlock);

    printf("main thread will exit\n");

    return 0;
}

  


关于读写锁的第二个程序,主要是测试读锁和写锁哪个更加优先的问题,linux默认的是读者优先

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

pthread_rwlock_t rwlock;

void *readers(void *arg)
{
    pthread_rwlock_rdlock(&rwlock);
    printf("readers %d got the lock\n",(int)arg);
    pthread_rwlock_unlock(&rwlock);
}

void *writes(void *arg)
{
    pthread_rwlock_wrlock(&rwlock);
    printf("write %d got the lock\n",(int)arg);
    pthread_rwlock_unlock(&rwlock);
}

int main(int argc,char *argv[])
{
    int retval,i;
    pthread_t writer_id,reader_id;
    pthread_attr_t attr;
    int nreadercount = 1;
    int nwritercount = 1;

    if(argc != 2){
         // error
         exit(1);
    }

    retval = pthread_rwlock_init(&rwlock,NULL);
    if(retval != 0){
        //error
        exit(2);
    }

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);


 //   pthread_rwlock_wrlock(&rwlock);
    pthread_rwlock_rdlock(&rwlock);
    for(i = 0; i < atoi(argv[1]);i++){
       if(random() % 2){
            pthread_create(&reader_id,&attr,readers,(void *)nreadercount);
            printf("create reader %d\n",nreadercount++);
       }else{
            pthread_create(&writer_id,&attr,writes,(void *)nwritercount);
            printf("create writer %d\n",nwritercount++);
       }
    }

  //  sleep(10);
    pthread_rwlock_unlock(&rwlock);
    sleep(10);
    return 0;
}

在上面的程序中如果有这个pthread_rwlock_wrlock(&rwlock);那么就算读者先加锁了,这时写者来了,那么再来的读者就不能加锁了,写者一直等到第一个读者结束后自己在加锁。理论是这样的,但是实际的结果,确实有点偏差,先看图


第6个读者先得到了锁,我感觉这个里面做了优化,前9个因为加锁的缘故只能先创建完之后再执行线程,最后一个readers 6,内核已经知道迟早readers 6要执行的,既然内核现在拿着这个reader 6,内核就顺便执行,才不管你有么有pthread_rwlock_wrlock(&rwlock)


在上面的程序中如果有这个pthread_rwlock_rdlock(&rwlock),运行结果



如果有什么问题,欢迎指正

2019-02-28 22:54:52 naibozhuan3744 阅读数 205

互斥锁是独占、互斥的,而读写锁中的读锁允许多个线程同时访问临界区,写锁跟互斥锁一样不允许多个线程同时访问临界区。当使用读锁占用临界区时,有读锁申请该临界区被阻塞,后面再有读锁申请访问该临界区时也会被阻塞。读写锁的创建跟互斥锁一样,都分为四个步骤:创建读锁或者写锁对象、初始化、上锁、解锁、销毁读锁/写锁对象。下面将通过一个实例展示读写锁用法。读写锁一般应用在大量并发请求的场合!

一、POSIX下读写锁调用实例(功能:互斥锁和读写锁速度大PK)

2.1在Linux指定目录下新建一个文件名为test.cpp的空白文件,在其中添加如下代码:

2.2在终端输入如下命令,结果如图所示:

g++ -o test test.cpp -lpthread

 

 

参考内容:

《Linux C与C++ 一线开发实践》 朱文伟,李建英著. -北京:清华大学出版社,2018  406-440页

2019-06-20 11:10:00 weixin_30726161 阅读数 1

线程的读写锁函数:

1,读写锁的初始化与销毁,静态初始化的话,可以直接使用PTHREAD_RWLOCK_INITIALIZER。

#include <pthread.h>
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
           const pthread_rwlockattr_t *restrict attr);
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

2,用读的方式加锁和尝试(没锁上就立即返回)加锁。

#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

3,用写的方式加锁和尝试(没锁上就立即返回)加锁。

#include <pthread.h>
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

4,解锁

#include <pthread.h>
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

多个进程在同时读写同一个文件,会发生什么?

例子1:用下面的例子的执行结果,观察多个进程在同时读写同一个文件,会发生什么。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define MAXLINE 100
#define FN "num1"

void my_lock(int fd){
  return;
}

void my_unlock(int fd){
  return;
}

int main(int args, char** argv){

  int fd;
  long i,seqno;
  pid_t pid;
  ssize_t n;
  char line[MAXLINE + 1];

  pid = getpid();
  fd = open(FN, O_RDWR, 0664);

  for(i = 0; i < 20; ++i){
    my_lock(fd);

    lseek(fd, 0L, SEEK_SET);
    n = read(fd, line, MAXLINE);
    line[n] = '\0';

    seqno = atol(line);
    printf("%s:pid = %ld, seq = %ld\n", argv[0], (long)pid, seqno);

    seqno++;

    snprintf(line, sizeof(line), "%ld\n", seqno);

    lseek(fd, 0L, SEEK_SET);
    write(fd, line, strlen(line));

    my_unlock(fd);
  }

  return 0;
}

执行方法:同时执行上面例子的程序2次,也就是2个进程同时读写同一个文件。

ubuntu$ ./flockmain1 & ./flockmain1 &

执行结果如下,发现2个进程同时读写,在①处开始,内核切换进程时,数字乱套了。

ubuntu$ ./flockmain1 & ./flockmain1 &
[1] 4760
[2] 4761
ubuntu$ ./flockmain1:pid = 4761, seq = 1
./flockmain1:pid = 4761, seq = 2
./flockmain1:pid = 4761, seq = 3
./flockmain1:pid = 4761, seq = 4
./flockmain1:pid = 4761, seq = 5
./flockmain1:pid = 4761, seq = 6
./flockmain1:pid = 4761, seq = 7
./flockmain1:pid = 4761, seq = 8
./flockmain1:pid = 4761, seq = 9
./flockmain1:pid = 4761, seq = 10   ------------①
./flockmain1:pid = 4760, seq = 10
./flockmain1:pid = 4761, seq = 11
./flockmain1:pid = 4761, seq = 12
./flockmain1:pid = 4761, seq = 13
./flockmain1:pid = 4761, seq = 14
./flockmain1:pid = 4761, seq = 15
./flockmain1:pid = 4761, seq = 16
./flockmain1:pid = 4761, seq = 17
./flockmain1:pid = 4761, seq = 18
./flockmain1:pid = 4761, seq = 19
./flockmain1:pid = 4761, seq = 20
./flockmain1:pid = 4760, seq = 11
./flockmain1:pid = 4760, seq = 12
./flockmain1:pid = 4760, seq = 13
./flockmain1:pid = 4760, seq = 14
./flockmain1:pid = 4760, seq = 15
./flockmain1:pid = 4760, seq = 16
./flockmain1:pid = 4760, seq = 17
./flockmain1:pid = 4760, seq = 18
./flockmain1:pid = 4760, seq = 19
./flockmain1:pid = 4760, seq = 20
./flockmain1:pid = 4760, seq = 21
./flockmain1:pid = 4760, seq = 22
./flockmain1:pid = 4760, seq = 23
./flockmain1:pid = 4760, seq = 24
./flockmain1:pid = 4760, seq = 25
./flockmain1:pid = 4760, seq = 26
./flockmain1:pid = 4760, seq = 27
./flockmain1:pid = 4760, seq = 28
./flockmain1:pid = 4760, seq = 29

为了解决上面的问题,必须对文件的内容进行加锁。

如何对文件内容加锁?

使用fcntl函数,它既可以锁整文件,也可以锁文件里的某段内容。通过结构体flock来指定要锁的范围。如果 whence = SEEK_SET;l_start = 0;l_len = 0;就是锁定整个文件。

struct flock {
               ...
               short l_type;    /* Type of lock: F_RDLCK,
                                   F_WRLCK, F_UNLCK */
               short l_whence;  /* How to interpret l_start:
                                   SEEK_SET, SEEK_CUR, SEEK_END */
               off_t l_start;   /* Starting offset for lock */
               off_t l_len;     /* Number of bytes to lock */
               pid_t l_pid;     /* PID of process blocking our lock
                                   (set by F_GETLK and F_OFD_GETLK) */
               ...
           };
  • F_SETLK:上锁。如果发现已经被别的进程上锁了,就直接返回-1,errno被设置成EACCES或者EAGAIN,不阻塞。
  • F_SETLKW:上锁。阻塞等待。
  • F_GETLK:得到锁的状态。

修改上面的函数my_lock,my_unlock。main函数不变。

例子2:

void my_lock(int fd){
  struct flock lock;
  lock.l_type = F_WRLCK;
  wlock.l_whence = SEEK_SET;
  lock.l_start = 0;
  lock.l_len = 0;
  
  fcntl(fd, F_SETLKW, lock);
}

void my_unlock(int fd){
  struct flock lock;
  lock.l_type = F_UNLCK;
  lock.l_whence = SEEK_SET;
  lock.l_start = 0;
  lock.l_len = 0;

  fcntl(fd, F_SETLK, lock);
}

执行结果如下,发现数字不乱套了。

ubuntu$ ./flockmain & ./flockmain &
[1] 4882
[2] 4883
ubuntu$ ./flockmain:pid = 4883, seq = 1
./flockmain:pid = 4883, seq = 2
./flockmain:pid = 4883, seq = 3
./flockmain:pid = 4883, seq = 4
./flockmain:pid = 4883, seq = 5
./flockmain:pid = 4883, seq = 6
./flockmain:pid = 4883, seq = 7
./flockmain:pid = 4883, seq = 8
./flockmain:pid = 4883, seq = 9
./flockmain:pid = 4883, seq = 10
./flockmain:pid = 4883, seq = 11
./flockmain:pid = 4883, seq = 12
./flockmain:pid = 4883, seq = 13
./flockmain:pid = 4883, seq = 14
./flockmain:pid = 4883, seq = 15
./flockmain:pid = 4883, seq = 16
./flockmain:pid = 4883, seq = 17
./flockmain:pid = 4883, seq = 18
./flockmain:pid = 4883, seq = 19
./flockmain:pid = 4883, seq = 20
./flockmain:pid = 4882, seq = 21
./flockmain:pid = 4882, seq = 22
./flockmain:pid = 4882, seq = 23
./flockmain:pid = 4882, seq = 24
./flockmain:pid = 4882, seq = 25
./flockmain:pid = 4882, seq = 26
./flockmain:pid = 4882, seq = 27
./flockmain:pid = 4882, seq = 28
./flockmain:pid = 4882, seq = 29
./flockmain:pid = 4882, seq = 30
./flockmain:pid = 4882, seq = 31
./flockmain:pid = 4882, seq = 32
./flockmain:pid = 4882, seq = 33
./flockmain:pid = 4882, seq = 34
./flockmain:pid = 4882, seq = 35
./flockmain:pid = 4882, seq = 36
./flockmain:pid = 4882, seq = 37
./flockmain:pid = 4882, seq = 38
./flockmain:pid = 4882, seq = 39
./flockmain:pid = 4882, seq = 40

到此为止,貌似解决了问题,但是如果同时执行例子1和例子2,结果如下,发现还是乱的。

也就是说在协作线程(cooperating processes)间,文件锁(也叫劝告性上锁)也起作用的。但是不完全不相关的进程中,文件锁也不起作用的。如何解决呢?使用强制性上锁。

ys@ys-VirtualBox:~/IPC$ ./flockmain1 & ./flockmain &
[1] 3602
[2] 3603
ys@ys-VirtualBox:~/IPC$ ./flockmain1:pid = 3602, seq = 1
./flockmain:pid = 3603, seq = 1
./flockmain:pid = 3603, seq = 2
./flockmain:pid = 3603, seq = 3
./flockmain:pid = 3603, seq = 4
./flockmain:pid = 3603, seq = 5
./flockmain:pid = 3603, seq = 6
./flockmain:pid = 3603, seq = 7
./flockmain:pid = 3603, seq = 8
./flockmain:pid = 3603, seq = 9
./flockmain:pid = 3603, seq = 10
./flockmain1:pid = 3602, seq = 2
./flockmain1:pid = 3602, seq = 3
./flockmain1:pid = 3602, seq = 4
./flockmain:pid = 3603, seq = 11
./flockmain:pid = 3603, seq = 12
./flockmain1:pid = 3602, seq = 5
./flockmain:pid = 3603, seq = 13
./flockmain1:pid = 3602, seq = 6
./flockmain1:pid = 3602, seq = 7
./flockmain1:pid = 3602, seq = 8
./flockmain:pid = 3603, seq = 14
./flockmain:pid = 3603, seq = 15
./flockmain1:pid = 3602, seq = 9
./flockmain1:pid = 3602, seq = 10
./flockmain:pid = 3603, seq = 16
./flockmain:pid = 3603, seq = 17
./flockmain1:pid = 3602, seq = 11
./flockmain:pid = 3603, seq = 18
./flockmain1:pid = 3602, seq = 12
./flockmain1:pid = 3602, seq = 13
./flockmain1:pid = 3602, seq = 14
./flockmain:pid = 3603, seq = 19
./flockmain:pid = 3603, seq = 20
./flockmain1:pid = 3602, seq = 15
./flockmain1:pid = 3602, seq = 16
./flockmain1:pid = 3602, seq = 17
./flockmain1:pid = 3602, seq = 18
./flockmain1:pid = 3602, seq = 19
./flockmain1:pid = 3602, seq = 20

第一个问题:假如一个文件被一个进程以读的方式锁定,并有另一个进程在等待读锁定解锁后,用写入的方式锁定,这时是否允许另一个进程的还以读的方式取得锁定?

用例子3来观察:

#include <time.h>
#include <sys/time.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

void gftime(char* buf){
  struct timeval tv;
  gettimeofday(&tv, NULL);
  long usec = tv.tv_usec;
  struct tm* tm = localtime(&tv.tv_sec);
  sprintf(buf, "%d:%d:%d.%ld",tm->tm_hour, tm->tm_min, tm->tm_sec,usec);

}

int main(){
  char buff[100] = {0};
 
  int fd = open("test.dat", O_RDWR | O_CREAT, 0664);

  struct flock lock;
  
  lock.l_type = F_RDLCK;
  lock.l_whence = SEEK_SET;
  lock.l_start = 0;
  lock.l_len = 0;
  fcntl(fd, F_SETLK, &lock);

  gftime(buff);
  printf("%s: parent has read lock\n", buff);

  //first child
  if(fork() == 0){

    char buf2[100] = {0};
    sleep(1);
    gftime(buf2);
    printf("%s: first child tries to obtain write lock\n", buf2);

    struct flock lock2;
    lock2.l_type = F_WRLCK;
    lock2.l_whence = SEEK_SET;
    lock2.l_start = 0;
    lock2.l_len = 0;
    fcntl(fd, F_SETLKW, &lock2);

    gftime(buf2);
    printf("%s: first child obtains write lock\n", buf2);

    sleep(2);

    lock2.l_type = F_UNLCK;
    lock2.l_whence = SEEK_SET;
    lock2.l_start = 0;
    lock2.l_len = 0;
    fcntl(fd, F_SETLK, &lock2);

    gftime(buf2);
    printf("%s: first child releases write lock\n", buf2);
    
    exit(0);
  }
  //secodn child
  if(fork() == 0){
    char buf1[100] = {0};
    sleep(3);
    gftime(buf1);
    printf("%s: second child tries to obtain read lock\n", buf1);

    struct flock lock1;
    lock1.l_type = F_RDLCK;
    lock1.l_whence = SEEK_SET;
    lock1.l_start = 0;
    lock1.l_len = 0;
    fcntl(fd, F_SETLKW, &lock1);

    gftime(buf1);
    printf("%s: second child obtains read lock\n", buf1);

    sleep(4);

    lock1.l_type = F_UNLCK;
    lock1.l_whence = SEEK_SET;
    lock1.l_start = 0;
    lock1.l_len = 0;
    fcntl(fd, F_SETLK, &lock1);

    gftime(buf1);
    printf("%s: second child release read lock\n", buf1);
    
    exit(0);
  }

  //parent process
  sleep(5);

  lock.l_type = F_UNLCK;
  lock.l_whence = SEEK_SET;
  lock.l_start = 0;
  lock.l_len = 0;
  fcntl(fd, F_SETLK, &lock);

  gftime(buff);
  printf("%s: parent releases read lock\n", buff);
  
  wait(NULL);
  wait(NULL);

  exit(0);
}

在ubuntu上执行结果:

17:49:44.348946: parent has read lock
17:49:45.350191: first child tries to obtain write lock
17:49:47.350155: second child tries to obtain read lock
17:49:47.350409: second child obtains read lock
17:49:49.349442: parent releases read lock
17:49:51.351197: second child release read lock
17:49:51.351582: first child obtains write lock
17:49:53.351689: first child releases write lock

第一个问题的答案:允许另一个进程的还以读的方式取得锁定

第二个问题:假如一个文件被一个进程以写的方式锁定,这时又有2个进程在等待这个锁的释放,其中一个进程是以写锁的方式等待,其中另一个进程是以读锁的方式等待,哪一个会优先取得锁?

用例子4来观察:

#include <time.h>
#include <sys/time.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

void gftime(char* buf){
  struct timeval tv;
  gettimeofday(&tv, NULL);
  long usec = tv.tv_usec;
  struct tm* tm = localtime(&tv.tv_sec);
  sprintf(buf, "%d:%d:%d.%ld",tm->tm_hour, tm->tm_min, tm->tm_sec,usec);

}

int main(){
  char buff[100] = {0};
 
  int fd = open("test.dat", O_RDWR | O_CREAT, 0664);

  struct flock lock;
  
  lock.l_type = F_WRLCK;
  lock.l_whence = SEEK_SET;
  lock.l_start = 0;
  lock.l_len = 0;
  fcntl(fd, F_SETLK, &lock);

  gftime(buff);
  printf("%s: parent has write lock\n", buff);

  //first child
  if(fork() == 0){

    char buf2[100] = {0};
    sleep(1);
    gftime(buf2);
    printf("%s: first child tries to obtain write lock\n", buf2);

    struct flock lock2;
    lock2.l_type = F_WRLCK;
    lock2.l_whence = SEEK_SET;
    lock2.l_start = 0;
    lock2.l_len = 0;
    fcntl(fd, F_SETLKW, &lock2);

    gftime(buf2);
    printf("%s: first child obtains write lock\n", buf2);

    sleep(2);

    lock2.l_type = F_UNLCK;
    lock2.l_whence = SEEK_SET;
    lock2.l_start = 0;
    lock2.l_len = 0;
    fcntl(fd, F_SETLK, &lock2);

    gftime(buf2);
    printf("%s: first child releases write lock\n", buf2);
    
    exit(0);
  }
  //secodn child
  if(fork() == 0){
    char buf1[100] = {0};
    sleep(3);
    gftime(buf1);
    printf("%s: second child tries to obtain read lock\n", buf1);

    struct flock lock1;
    lock1.l_type = F_RDLCK;
    lock1.l_whence = SEEK_SET;
    lock1.l_start = 0;
    lock1.l_len = 0;
    fcntl(fd, F_SETLKW, &lock1);

    gftime(buf1);
    printf("%s: second child obtains read lock\n", buf1);

    sleep(4);

    lock1.l_type = F_UNLCK;
    lock1.l_whence = SEEK_SET;
    lock1.l_start = 0;
    lock1.l_len = 0;
    fcntl(fd, F_SETLK, &lock1);

    gftime(buf1);
    printf("%s: second child release read lock\n", buf1);
    
    exit(0);
  }

  //parent process
  sleep(5);

  lock.l_type = F_UNLCK;
  lock.l_whence = SEEK_SET;
  lock.l_start = 0;
  lock.l_len = 0;
  fcntl(fd, F_SETLK, &lock);

  gftime(buff);
  printf("%s: parent releases write lock\n", buff);
  
  wait(NULL);
  wait(NULL);

  exit(0);
}

在ubuntu上执行结果:

17:49:29.796599: parent has write lock
17:49:30.797099: first child tries to obtain write lock
17:49:32.796885: second child tries to obtain read lock
17:49:34.796868: parent releases write lock
17:49:34.796987: second child obtains read lock
17:49:38.797148: second child release read lock
17:49:38.797297: first child obtains write lock
17:49:40.797727: first child releases write lock

第二个问题的答案:没有准确答案。在Ubuntu上的执行结果上看,读锁优先了,但是,可能在别的环境上又是写锁优先。按道理来说应该写锁优先吧?

c/c++ 学习互助QQ群:877684253

1414315-20181106214320230-961379709.jpg

本人微信:xiaoshitou5854

转载于:https://www.cnblogs.com/xiaoshiwang/p/11057552.html

没有更多推荐了,返回首页