单线程写/多线程读方式访问同一链表,需不需要加锁?

时间:2023-02-22 12:18:47
多线程访问同一链表场景:  
  1.多个线程同时读(查找/遍历)  
  2.单线程写(添加/删除/更新)

 考虑到多线程的效率,我不想加锁
基于整数赋值都是原子操作的前提,单写多读延迟释放
 例如:
 A->B->C(双向链表)
 <- <-

1.删除B时,原子操作修改AC指针,延迟释放B
2.更新B时,先生成一个新节点B",原子修改AC指针到B",延迟释放B
下面测试程序验证感觉没有问题,但是延迟释放的契机无法把握,
请教高手有没有更好的办法?
测试程序:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
#include <sys/timeb.h>
#include <inttypes.h>
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>  
#include <sys/time.h>


struct Node
{
  Node * next;
  int key;
  char val[50];
  Node()
  {
   memset(this,0,sizeof(Node));
  }
};
class MyList
{
public:
MyList()
{
  m_pHead = new Node;
  assert(m_pHead!=NULL);
  m_pHead->next = NULL;
  m_pHead->key = 0;
  //m_pHead->val = 0;
}
~MyList()
{
  while(m_pHead != NULL)
   {
    Node * pTmp = m_pHead->next;
    delete m_pHead;
    m_pHead = pTmp;
  }
}

/*int Get(int aikey)
{
  Node * pTmp = m_pHead->next;
  while(pTmp != NULL)
   {
     if(pTmp->key == aikey)
     {
       return pTmp->val;
     }else
     {
       pTmp = pTmp->next;
     }
  }
  return 0;
}*/

char* Get(int aikey)
{
  Node * pTmp = m_pHead->next;
  while(pTmp != NULL)
   {
     if(pTmp->key == aikey)
     {
       return pTmp->val;
     }else
     {
       pTmp = pTmp->next;
     }
  }
  return NULL;
}

int Set(int aikey, int aival)
{
  Node * pTmp = new Node;
  assert(pTmp!=NULL);
  pTmp->key = aikey;
  //pTmp->val = aival;
  sprintf(pTmp->val,"%d",aival);
  pTmp->next = m_pHead->next;
  m_pHead->next = pTmp; //atomic?
  return 1;
}

int Del(int aikey)
{
  Node * pfront = m_pHead;
  Node * pnext = m_pHead->next;
  
  while(pnext != NULL)
   {
     if(pnext->key == aikey)
     {
       pfront->next = pnext->next; //atomic?
       //free(pnext); //wait
       return 1;
     }else
     {
       pfront = pnext;
       pnext = pnext->next;
     }
  }
  return 0;  
}

int Upd(int aikey,int aival)
{
//create new node
  Node * pTmp = new Node;
  assert(pTmp!=NULL);
  pTmp->key = aikey;
  sprintf(pTmp->val,"%d",aival);
  //insert head
  pTmp->next = m_pHead->next;
  m_pHead->next = pTmp; //atomic?
  
  //remove old key
  return Del(aikey);
}

private:
  Node *m_pHead;
};


int g_threadnum;
MyList loList;
int g_nodenum;

static void create_worker(void *(*func)(void *), void *arg) {
    pthread_t       thread;
    pthread_attr_t  attr;
    int             ret;

    pthread_attr_init(&attr);


    if ((ret = pthread_create(&thread, &attr, func, arg)) != 0) {
        fprintf(stderr, "Can't create thread: %s\n",
                strerror(ret));
        exit(1);
    }
}

static void *ReadFunc(void *arg)
{
  MyList *mc = (MyList*)arg;
  while(1)
  {
    //usleep(g_sleeptime);
    int liRand = (rand()%g_nodenum) +1 ;
    int liVal = atoi(mc->Get(liRand));
    if(liVal)
    {
      if((liVal % liRand) !=0 )
      {
        printf("Get Error Key=%d Val=%d\n",liRand,liVal);
        exit(1);
      }else
      {
       ;//printf("Get Success Key=%d Val=%d\n",liRand,liVal);
      }
    }
  }
  exit(0);
}

static void *WriteFunc(void *arg)
{
  MyList *mc = (MyList*)arg;
  int licount = 0;
  while(1)
  {
    //usleep(g_sleeptime);
    int liRand = (rand()%g_nodenum) +1 ;
    if(mc->Del(liRand))
{
  licount++;
  printf("Del Key=%d\n",liRand);
  if(licount%1000==0) 
  {
   printf("Del Count[=%d]\n",licount);
  }
}
  }
  exit(0);
}

static void *UpdateFunc(void *arg)
{
  MyList *mc = (MyList*)arg;
  int licount = 0;
  while(1)
  {
    //usleep(g_sleeptime);
    int liRand = (rand()%g_nodenum) +1 ;
    int liplus = (liRand%10) +1;
    if(mc->Upd(liRand,liRand*liplus))
{
  licount++;
  //printf("Upd Key=%d\n",liRand);
  if(licount%1000==0) 
  {
   printf("Upd Count[=%d]\n",licount);
  }
}
  }
  exit(0);
}

int main(int argc, char** agrv)
{
  int i = 0;
  g_threadnum = atoi(agrv[1]);
  g_nodenum = atoi(agrv[2]);
  for(i=0; i< g_nodenum; i++)
  {
    loList.Set(i+1,i+1);
  }
  for(int i=0; i< g_threadnum; i++)
  {
    create_worker(ReadFunc,&loList);
  }
  //create_worker(WriteFunc,&loList);
  create_worker(UpdateFunc,&loList);
  while(1)
  {
    usleep(10);
  }
  return 0;
}

8 个解决方案

#1



需要加锁。

#2



linux 下,建议读写锁,比较快。

#3


这都不想用锁。。。。。
linux下直接读写锁,windows下自己实现个读写锁大概效率高点,可以百度

#4


如果是写一个字节,不用加锁。(^_^)

#5


引用 4 楼  的回复:
如果是写一个字节,不用加锁。(^_^)

如果只是修改,单一数据4个字节以下也不用加(32位系统),但是删除和添加数据是不行的

#6


无论写一个字节还是一位 都需要加锁

#7


引用 6 楼  的回复:
无论写一个字节还是一位 都需要加锁

no,原子操作无需加锁

#8


加呗!总不会错!

#1



需要加锁。

#2



linux 下,建议读写锁,比较快。

#3


这都不想用锁。。。。。
linux下直接读写锁,windows下自己实现个读写锁大概效率高点,可以百度

#4


如果是写一个字节,不用加锁。(^_^)

#5


引用 4 楼  的回复:
如果是写一个字节,不用加锁。(^_^)

如果只是修改,单一数据4个字节以下也不用加(32位系统),但是删除和添加数据是不行的

#6


无论写一个字节还是一位 都需要加锁

#7


引用 6 楼  的回复:
无论写一个字节还是一位 都需要加锁

no,原子操作无需加锁

#8


加呗!总不会错!