静态引用可以用作“标志”吗?使用原子链表
本文关键字:链表 标志 引用 静态 | 更新日期: 2023-09-27 18:18:05
在c++中使用原子时,我可以在指针未使用的地址位中打包其他变量。我可以自动改变标志,计数器等与指针一起。在c#中,这并不容易。我认为我可以使用具有特殊含义的静态引用,但我有点担心gc在压缩堆时移动引用。例如,在这个示例代码中,可以通过使用静态引用s_cantAddWork自动将链表标记为关闭添加。我的问题是,我是否需要担心gc移动s_cantAddWork?我需要使用Fixed吗?到目前为止,在长时间运行这样的代码之后,它似乎是安全的。不过我想听听专家的意见。
using System;
using System.Threading;
namespace Testola {
class Program {
static void Main(string[] args) {
// pile = null
var w = new CWork();
if (!AtomicAddWork(w))
Console.WriteLine("Cant add work!"); // not hit
// pile = 1
w = new CWork();
if (!AtomicAddWork(w))
Console.WriteLine("Cant add work!"); // not hit
// pile = 2,1
w = AtomicGetWork();
// pile = 1
w = new CWork();
if (!AtomicAddWork(w))
Console.WriteLine("Cant add work!"); // not hit
// pile = 3,1
// remove everything from pile and disable adding.
w = AtomicGetAllWorkAndLockOutOtherThreadsFromAddingMoreWork();
// pile = s_cantAddWork
w = new CWork();
if (!AtomicAddWork(w))
Console.WriteLine("Cant add work!"); // HITS THIS!
}
public class CWork {
static int s_cItems = 0;
public static CWork s_cantAddWork = new CWork();
public CWork next;
public string data = (s_cItems++).ToString();
}
static volatile CWork m_workPile;
static bool AtomicAddWork(CWork work) {
while (true) {
var Old = m_workPile;
// WHAT HAPPENS HERE IF GC MOVES s_cantAddWork? <<------------------
// I assume Old is moved too, and all threads are stopped, so my atomic stuff will still work.
if (Old == CWork.s_cantAddWork)
return false;
work.next = Old;
if (Interlocked.CompareExchange(ref m_workPile, work, Old) == Old)
return true; // success
work.next = null;
}
}
static CWork AtomicGetWork() {
while (true) {
var Old = m_workPile;
if (Old == null)
return null;
if (Interlocked.CompareExchange(ref m_workPile, Old.next, Old) == Old)
return Old; // success
}
}
static CWork AtomicGetAllWorkAndLockOutOtherThreadsFromAddingMoreWork() {
while (true) {
var Old = m_workPile;
if (Interlocked.CompareExchange(ref m_workPile, CWork.s_cantAddWork, Old) == Old)
return Old; // success
}
}
}
}
安全。
引用不同于地址指针。当c#移动内存时,它将使您的代码透明,除非您使用不安全的操作,如operator的地址。
若要获取一元表达式的地址(计算结果为固定变量),请使用寻址运算符:[MSDN]
int* p = &number;
如果使用地址,则需要使用unsage关键字将代码标记为不安全。此时,您可能希望使用fixed语句来固定地址。
还有,看一下这个问题的更多细节:
在压缩发生后GC如何更新引用