• 多线程队列——锁


                 最近看了一篇关于多线程队列的文章(http://www.parallellabs.com/2010/10/25/practical-concurrent-queue-algorithm/)觉得很不错,但是对其中有一些地方还不是很懂,于是决定自己根据《C++ Primer 4》的队列模版来实现一个队列。

            代码如下:

    #pragma once
    #define _AFXDLL
    #include<iostream>
    #include <pthread.h>
    #include<afxmt.h>
    template<class type> class Line;
    template<class type>
    class LineItem
    {
    public:
    	friend class Line<type>;
    	LineItem():item( ),next(0){}
    	LineItem(const type&t):item(t),next(0){}
    	~LineItem(){}
    	type item;   
    	LineItem *next;
    };
    template<class type>
    class Line
    {
    public:
    	Line(void):head(0),tail(0),num(0),h_lock(FALSE, __T("head lock"), NULL),
    		t_lock(FALSE, __T("tail lock"), NULL){InitializeCriticalSection(&cs);}
    	Line(Line&l):head(0),tail(0)
    	{copy_elems(l);}
    	Line& operator=(Line& line)
    	{
    		if(line.isempty())
    		  return *this;
    		LineItem<type> *p=line.front();
    		LineItem<type> *pt=this->front();
    	   while(p->next!=NULL)
    	   { 
    		  pt->item=p->item;
    		  pt=pt->next=new LineItem<type>;	
    		  p=p->next;
    	   }
    	  p->item=pt->item;
    	  p->next=NULL;
    	  tail=p;
    	  num=line.count();
    	  return *this;
    	}
    	~Line(void){destroy();}
    	LineItem<type>* front()
    	{
    		if(num==0)
    		{
    			return 0;
    		}
    		if(num==1)
    		{	
    			return head;
    		}
    		else
    		{			
    			return head;			
    		}
    	}
    	LineItem<type>* back(){return tail;}
    	void push(const type& item)
    	{
    		  t_lock.Lock();
    		  LineItem<type> *p=new LineItem<type>(item);
    		  if(tail==0)
    			  head=tail=p;
    		  else
    		  {
    			 tail->next=p;
    			 tail=p;
    		  }
    		  EnterCriticalSection(&cs);
    		  ++num;
    		  LeaveCriticalSection(&cs); 
    		  t_lock.Unlock();
    	}
    	void pop()
    	{		
    		if (num==1)
    		{
    			 t_lock.Lock();
    			 head=tail=0;
    			 head=0;
    			 EnterCriticalSection(&cs);
    			 --num;
    			 LeaveCriticalSection(&cs); 
    			 t_lock.Unlock();
    		}
    		else
    		{
    			h_lock.Lock();
    			if (head==0)
    			{
    			   h_lock.Unlock();
    			   return;
    			}
    			LineItem<type> *p=head;
    			head=head->next;
    			delete p;
    			EnterCriticalSection(&cs);
    			--num;
    			LeaveCriticalSection(&cs);  			
    		}
    		h_lock.Unlock();
    	}
    	bool isempty() const
    	{
    		return head==0;
    	}
    	std::size_t count()
    	{
    	   if(isempty())
    		 return 0;
    	   LineItem<type> *p=head;
    	   std::size_t n=0;
    	   while(p)
    	   {
    		 ++n;
    		 p=p->next;
    	   }
    	  return n;
    	}
    
    	void clear()
    	{
    		destroy();
    	}
    
    private:
    	LineItem<type> *head;
    	LineItem<type> *tail;
    	int num;
    	CMutex h_lock,t_lock;
    	CRITICAL_SECTION cs;
    	void destroy()
    	{
    		while(!isempty())
    		pop();
    		num=0;
    	}
    	void copy_elems(Line& l)
    	{
    		for(LineItem<type> *p=l.front();p;p=p->next)
    		 push(p->item);
    		num=l.count();
    	}
    };
              本文针对的是队列的pop 和 push 操作,其他问题没有考虑到,所以这个队列仅供分析pop和push 的并发操作。

               这个模版用了两个锁,一个队列头锁h_lock 和一个队列尾锁t_lock ,为什么要用两个锁呢,因为,当一个队列在push操作时,队列里可能不止一个元素,这时,如果只用一个锁,其他线程只能等待,到你push完后,其他线程才能执行操作;如果用两个锁的话,可以让两个线程一起分别针对头尾进行操作,因为他们没有用到相同的资源,所以没必要互斥,这样效率会跟高。

                如果,队列是空的或者只有一个元素,会发生什么情况呢?先看代码:

    void pop()
    	{		
    		if (num==1)
    		{
    			 t_lock.Lock();
    			 head=tail=0;
    			 head=0;
    			 EnterCriticalSection(&cs);
    			 --num;
    			 LeaveCriticalSection(&cs); 
    			 t_lock.Unlock();
    		}
    		else
    		{
    			h_lock.Lock();
    			if (head==0)
    			{
    			   h_lock.Unlock();
    			   return;
    			}
    			LineItem<type> *p=head;
    			head=head->next;
    			delete p;
    			EnterCriticalSection(&cs);
    			--num;
    			LeaveCriticalSection(&cs);  			
    		}
    		h_lock.Unlock();
    	}
    void push(const type& item)
    	{
    		  t_lock.Lock();
    		  LineItem<type> *p=new LineItem<type>(item);
    		  if(tail==0)
    			  head=tail=p;
    		  else
    		  {
    			 tail->next=p;
    			 tail=p;
    		  }
    		  EnterCriticalSection(&cs);
    		  ++num;
    		  LeaveCriticalSection(&cs); 
    		  t_lock.Unlock();
    	}

               如果队列为空,头尾指针没有指向相同的空间,因为头尾指针都为空 (head=tail=Null,它们的值相同) 这时,如果两个线程一个要push ,一个要pop,他们是不会发生互斥的,因为它们可以分别判断头尾指针是否为0 ,如果为0 ,就分别释放头尾两个锁。

              如果队列只有一个元素,这时两个线程分别执行push 和 pop 操作时,就会发生死锁,因为头指针和尾指针都指向同一个元素,这样会降低效率。(这时,前面介绍的那篇文章的解决方法是,在队列头前再加入一个元素,做头指针,当队列为空时,队列只有一个头元素,具体情况请看那篇文章。)我的方法是,根据计数器(num)的值来判断要用一个锁,还是用两个锁,当队列只有一个元素时,即num==1时,push 和 pop 操作只用一个锁,即t_lock; 当队列不止一个元素时,就用两个锁。具体看上面代码。计数器num是用来判断用多少个锁的依据。谢谢!

              参考:http://www.parallellabs.com/2010/10/25/practical-concurrent-queue-algorithm/

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    Python环境下如何安装爬虫需求的一些库
    (转)在.net中检索HTTP请求
    MyEclipse反编译插件下载地址
    Intellij Idea创建SpringBoot项目使用内置Tomcat能成功访问,将项目war包部署至第三方Tomcat访问出现404问题及解决方案
    解决Windows 10 Tomcat命令行窗口中文乱码
    springboot打包错误:Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0...
    JPA使用IDEAR生成映射实体
    EclipseJVM调优设置
    SpringBoot eureka 工作原理(转 Eureka服务发现注册之使用ip地址注册eureka服务详解)
    oracle行转列/列转行/字符串拆分 测试
  • 原文地址:https://www.cnblogs.com/Rex7/p/4752591.html
Copyright © 2020-2023  润新知