Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/267.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# C中指针入/减的线程安全方法#_C#_Pointers_Thread Safety_Clr - Fatal编程技术网

C# C中指针入/减的线程安全方法#

C# C中指针入/减的线程安全方法#,c#,pointers,thread-safety,clr,C#,Pointers,Thread Safety,Clr,奇妙的联锁类提供了这个超负荷的增量: Interlocked.Increment(ref int loc); 这看起来真的很接近我要找的。只是,我所拥有的不是变量本身,而是指向它的指针。因此,我不能使用ref,但需要重载: // does not exist: Interlocked.Increment(int* loc); 有解决办法吗?通过C#中的地址高效且线程安全地增加值的任何其他方法 所以我们可以做内部静态外部增量(ref int*loc)足够容易;相应的本机函数看起来是可写的

奇妙的联锁类提供了这个超负荷的增量:

Interlocked.Increment(ref int loc); 
这看起来真的很接近我要找的。只是,我所拥有的不是变量本身,而是指向它的指针。因此,我不能使用
ref
,但需要重载:

// does not exist: 
Interlocked.Increment(int* loc); 

有解决办法吗?通过C#中的地址高效且线程安全地增加值的任何其他方法

所以我们可以做
内部静态外部增量(ref int*loc)足够容易;相应的本机函数看起来是可写的:

int *Increment(int **loc)
{
    return (int*)InterlockedAdd((int *)loc, sizeof(int));
}
但你不能用它。生成的代码类似于:

int *locus = Interlocked.Increment(loc);
if (locus < base + length)
{
    // do something with locus
}
int*轨迹=联锁增量(loc);
if(轨迹<基准+长度)
{
//做点什么
}
但这实际上是未定义的。若loc增加太多次,它可能会溢出,轨迹最终指向一个非常低的地址。(base可能位于用户内存的顶部…)



另一方面,如果您有一个指向要递增的整数的指针;只需P/调用对
InterlockedIncrement
的调用即可。哎呀;不在64位上;必须构建一个很小的C dll来获取其内在特性。

编辑:问题的实际答案要简单得多。假设
p
是要递减的变量的
int*
。至少C#编译器允许对解引用的p直接执行
ref

Threading.Interlocked.Decrement(ref (*p)); 
从哪里来

int local = 10; 
IntPtr p = (IntPtr)(&local); 
... 
// decrement local via p
Threading.Interlocked.Decrement(ref (*(int*)p)); 
请注意,使用interlocked类(
interlocked.Decrement
)很重要,因为JIT为其生成有效的代码。(但效率不如直接使用
ref local


答案的这一部分现在已经过时了。但是,我把它留在这里,因为将本地文件包装到
结构中的技术无论如何都可能有帮助

仅当您可以选择修改代码和“指针”的语义时,此答案才适用。确切地说,它并没有回答最初的问题,但应该是有帮助的

除了p/Invoke本机函数的选项(及其缺点:p/Invoke开销、非托管函数维护开销)之外,还有另一个选项。它允许直接使用
联锁
类。JIT编译器将为它生成更高效的代码,我们将保存P/Invoke调用

该解决方案的灵感来源于以下线索,实际上是答案的无耻副本:

下面的代码演示如何从多个线程原子地递减本地堆栈分配的
int
计数器。它在托管堆上没有任何其他对象,也没有任何更高级的同步对象。在应用于生产代码之前,请确保针对常见风险保护此方法

internal struct DownCounter {
    internal int value;

    public DownCounter(int initialValue) {
        value = initialValue;
    }

    public int Decrement() {
        return Interlocked.Decrement(ref value);
    }
}

unsafe class Program {
    static void Main(string[] args) {
        // the local stack allocated counter, to be shared by all threads
        DownCounter counter = new DownCounter(10);

        // some work for the threads
        Action<object> work = (c) => {
            double a = 1;
            for (int i = 0; i < 1 << 26; i++) {
                // do stuff here 
                a = a % i * a % (i + 1);
            }
            // decrement the main thread's stack counter
            int d = (*((DownCounter*)(IntPtr)c)).Decrement();
            Console.WriteLine($"Decremented: {d}");
        };

        // start 10 threads, each decrements the local, stack allocated(!) counter
        for (int i = 0; i < 10; i++) {
            Task.Factory.StartNew(work, (IntPtr)(&counter));
        }

        // poor mans 'spin wait' on the local counter
        while (counter.value > 0) {
            Thread.Sleep(100);
            Console.WriteLine($"# threads running: {counter.value}");
        }
        Console.Read();
    }
}
内部结构下行计数器{
内部int值;
公共下行计数器(int initialValue){
值=初始值;
}
公共整数减量{
返回联锁减量(参考值);
}
}
不安全类程序{
静态void Main(字符串[]参数){
//本地堆栈分配的计数器,由所有线程共享
下行计数器=新下行计数器(10);
//有些工作线程
行动工作=(c)=>{
双a=1;

对于(int i=0;i<1)您是否考虑过使用它来代替您自己的同步程序?这听起来真的像是更新您的问题,解释您正在做什么,您需要编写一个没有分配的同步程序,可能有更好的解决方案。您无法运行(real)NET应用程序没有一个新的
,那么问题出在哪里呢?您只需要为所有线程创建一个同步对象。一个
信号量
或一个
倒计时事件
。因此,您愿意承担主线程在该变量上浪费数千个周期的成本,但不愿意承担cos分配一个通常不需要数千个周期的小对象的方法?@HaymoKutschbach祝你在.NET应用程序中好运。:)关闭线程之间共享的单个int不会导致多个分配(如果变量被提升到静态字段中,则不会导致任何分配)因此,不清楚您为什么要避免这种情况?您是否查看了生成的IL以了解闭包是如何生成的?PInvoke听起来很有希望。我这样做了,它正在32位上工作。不过,我仍在与64位进行斗争。没有这样的导出“InterlocatedDecrement”或“InterlocatedExchangeAdd”(这似乎是Intelock使用的。真的是递减)在64位kernel32.dll中。谷歌说这始终是作为内在的。不确定这在64位.NET中是如何工作的。可能是一些CLR欺骗?@HaymoKutschbach:他们必须有一个本机函数来完成它。这里有一个关于kernel32.dll的相关问题。除非还有另一个
InterlocatedDecrement
留给comp某些dll中的不适应性(?)对于64位而言,@Joshua建议的小型非托管C辅助dll似乎是一条可行之路。人们可能会决定保存该dll,并使用带有x64汇编指令的byte[]数组在运行时构造委托(非托管函数指针)(我不会建议这样冒险的尝试…)