DIOCP开源项目-Delphi高性能无锁队列(lock-free)

简介: 最近想在DIOCP中加入任务调度线程,DIOCP的工作线程作为生产者(producer)将接受到的数据对象,投递到任务调度线程中,然后统一进行分配。然而这一切都需要一个队列, 这几天都在关注无锁队列。   [队列] 首先是一个队列,简单的队列就是,生产者把数据压入队列(push), 消费者通过队列Pop出数据进行处理。

最近想在DIOCP中加入任务调度线程,DIOCP的工作线程作为生产者(producer)将接受到的数据对象,投递到任务调度线程中,然后统一进行分配。然而这一切都需要一个队列, 这几天都在关注无锁队列。

 

[队列]

首先是一个队列,简单的队列就是,生产者把数据压入队列(push), 消费者通过队列Pop出数据进行处理。

简单的队列就是提供Push,和Pop函数。

我们用一个链表来存储数据。Head ->data01->data02...data_n->Tail, 每个数据块的结构如下

type
  PVarQueueBlock = ^TVarQueueBlock;
  TVarQueueBlock = packed record
    value:Variant;
    next:PVarQueueBlock;
  end;

 

1.在进行Push压入数据时压入将Tail.next指向新压入的数据块, 然后用新的数据块做Tail

procedure TSimpleQueue.pushQueue(pvData: PVarQueueBlock);
begin
  if FHead = nil then
  begin
    FHead = pvData;
    FTail := FHead;
  end else
  begin
    FTail.next := pvData;
    FTail := pvData;
  end;
  Inc(FCount);
end;

 

 

2.在进行Pop数据时把Head数据块取出,然后用Head数据块指向的下一块当作Head.

function TSimpleQueue.popQueue: PVarQueueBlock;
var
  lvTemp, lvRet:PVarQueueBlock;
begin
  lvTemp := FHead;
  if (lvTemp = nil) then
  begin        //没有任何可以Pop出的值
    Result := nil;
    exit;
  end;

  //
  FHead := FHead.next;

  Dec(FCount);
  Result :=lvTemp;
end;

 

上面就是简单的队列

 

[无锁队列]

上面的实现的队列在多线程情况下是不安全的。如果要在多并发下队列要进行加锁,在push和pop时加锁也是一种办法。可以直接用临界就可以了,但是我们要做的是无锁队列

 

首先记住多并发设计规则:决不要假设任何代码会连续执行

 

上面的push操作

FTail.next := pvData;
FTail := pvData;

也许执行了FTail.next:=pvData后,会被另外的线程抢走,然后FTail进行了新的赋值,这样在进行FTail := pvData;这样整个数据链条就会被破坏。

如果这两行我们能一次行完成,这样就可以实现无锁操作了,这样我们需要引入原子操作.Interlocked中的函数。

 

说无锁其实不太确切,只是锁的粒度小了。我们是使用api的InterlockedCompareExchange函数来实现的。

查一下MSDN

http://msdn.microsoft.com/en-us/library/windows/desktop/ms683560(v=vs.85).aspx

LONG __cdecl InterlockedCompareExchange(
  _Inout_  LONG volatile *Destination,
  _In_     LONG Exchange,
  _In_     LONG Comparand
);

Parameters

Destination [in, out]

A pointer to the destination value.

Exchange [in]

The exchange value.

Comparand [in]

The value to compare to Destination.

Return value

The function returns the initial value of the Destination parameter.

 

大概解释一下。这个函数是比较后进行交换。第一个参数是要存放目的的数据,第二个是交换数据,第三个是比较数据(与第一个比较), 如果交换返回的数据和第三个参数一样。

 

 

这样就可以在push和pop一步完成。

这里贴出push的pop操作

procedure TVarQueue.pushQueue(pvData: PVarQueueBlock);
var
  lvTemp:PVarQueueBlock;
  lvPointer:Pointer;
begin
  while True do
  begin
     lvTemp := FTail;
     while lvTemp.next <> nil do lvTemp := lvTemp.next;
     if InterlockedCompareExchangePointer(Pointer(lvTemp.next), Pointer(pvData), nil) = nil then
     begin
       break;
     end;
  end;
  FTail := pvData;
  Inc(FCount);
end;
function TVarQueue.popQueue: PVarQueueBlock;
var
  lvTemp, lvRet:PVarQueueBlock;
  lvPointer:Pointer;
begin
  ///为了方便 队列中始终保留一个FHead数据块
  ///  也就是说FHead指向的下一个数据块才是第一个数据块
  ///

  while True do
  begin
    lvTemp := FHead;
    if (lvTemp = nil) or (lvTemp.next = nil) then
    begin        //没有任何可以Pop出的值
      Result := nil;
      exit;
    end;
    if InterlockedCompareExchangePointer(Pointer(FHead), lvTemp.next, lvTemp) = lvTemp then
    begin
      break;
    end;
  end;
  Dec(FCount);
  lvRet := lvTemp.next;
  Result := lvRet;
  lvTemp.next := nil;
  Dispose(lvTemp);
  //返回的是head.next
end;

 

后续我会上传完整的代码到DIOCP项目中。

如有漏洞,敬请指出。欢迎假如DIOCP群讨论

 

 

 

目录
相关文章
|
6月前
|
安全 Go API
go语言中的Atomic操作与sema锁
在并发编程中,确保数据一致性和程序正确性是关键挑战。Go语言通过协程和通道提供强大支持,但在需精细控制资源访问时,Atomic操作和sema锁变得至关重要。Atomic操作确保多协程环境下对共享资源的访问是不可分割的,如`sync/atomic`包中的`AddInt32`等函数,底层利用硬件锁机制实现。sema锁(信号量锁)控制并发协程数量,其核心是一个uint32值,当大于零时通过CAS操作实现锁的获取与释放;当为零时,sema锁管理协程休眠队列。这两种机制共同保障了Go语言并发环境下的数据完整性和程序稳定性。
|
安全 Go 数据安全/隐私保护
Golang 语言中基础同步原语 Mutex 和 RWMutex 的区别
Golang 语言中基础同步原语 Mutex 和 RWMutex 的区别
117 0
|
存储 程序员 Go
Go指针探秘:深入理解内存与安全性
Go指针探秘:深入理解内存与安全性
97 0
|
9月前
|
设计模式 缓存 安全
一篇文章带你吃透Go语言的Atomic和Channel--实战方法
一篇文章带你吃透Go语言的Atomic和Channel--实战方法
140 0
|
存储 缓存 Java
【项目设计】高并发内存池—tcmalloc核心框架学习(一)
【项目设计】高并发内存池—tcmalloc核心框架学习
532 1
|
存储 安全 Linux
【项目设计】高并发内存池—tcmalloc核心框架学习(三)
【项目设计】高并发内存池—tcmalloc核心框架学习
269 1
|
存储 缓存 算法
【项目设计】高并发内存池—tcmalloc核心框架学习(二)
【项目设计】高并发内存池—tcmalloc核心框架学习
250 1
|
并行计算 安全 Go
Golang并发编程技术:解锁Go语言的并行潜力
本文将介绍Golang中的一些关键特性和技术,以帮助您更好地理解和利用Go语言的并发编程潜力。
109 0
|
存储 缓存 BI
|
存储 程序员 编译器
保姆级SGI STL内存池源码刨析教程
保姆级SGI STL内存池源码刨析教程
保姆级SGI STL内存池源码刨析教程

热门文章

最新文章