计算多个线程中的内容

时间:2015-05-19 07:47:11

标签: c# multithreading thread-safety thread-local-storage

在我的.NET程序中,我想计算一段代码被击中的次数。为了使它更具挑战性,我的代码通常在多个线程中执行,我无法控制线程的创建/销毁(并且不知道它们何时被创建)......它们甚至可以被合并。说:

class Program
{
    static int counter = 0;

    static void Main(string[] args)
    {
        Stopwatch sw = Stopwatch.StartNew();

        Parallel.For(0, 100000000, (a) =>
            {
                Interlocked.Increment(ref counter);
            });

        Console.WriteLine(sw.Elapsed.ToString());
    }
}

由于性能计数器和方法遭遇了很多次,我想使用正常的'变量与原子/互锁整数形成对比。因此,我的第二次尝试是将threadlocal存储与IDisposable结合使用以加快速度。因为我无法控制创建/销毁,所以我必须跟踪存储变量:

class Program
{
    static int counter = 0;

    // I don't know when threads are created / joined, which is why I need this:
    static List<WeakReference<ThreadLocalValue>> allStorage = 
        new List<WeakReference<ThreadLocalValue>>();

    // The performance counter
    [ThreadStatic]
    static ThreadLocalValue local;

    class ThreadLocalValue : IDisposable
    {
        public ThreadLocalValue()
        {
            lock (allStorage)
            {
                allStorage.Add(new WeakReference<ThreadLocalValue>(this));
            }
        }

        public int ctr = 0;

        public void Dispose()
        {
            // Atomic add and exchange
            int tmp = Interlocked.Exchange(ref ctr, 0); // atomic set to 0-with-read
            Interlocked.Add(ref Program.counter, tmp); // atomic add
        }

        ~ThreadLocalValue()
        {
            // Make sure it's merged.
            Dispose();
        }
    }

    // Create-or-increment
    static void LocalInc()
    {
        if (local == null) { local = new ThreadLocalValue(); } 
        ++local.ctr;
    }

    static void Main(string[] args)
    {
        Stopwatch sw = Stopwatch.StartNew();

        Parallel.For(0, 100000000, (a) =>
            {
                LocalInc();
            });

        lock (allStorage)
        {
            foreach (var item in allStorage)
            {
                ThreadLocalValue target;
                if (item.TryGetTarget(out target))
                {
                    target.Dispose();
                }
            }
        }

        Console.WriteLine(sw.Elapsed.ToString());

        Console.WriteLine(counter);
        Console.ReadLine();
    }
}

我的问题是:我们能更快和/或更漂亮吗?

1 个答案:

答案 0 :(得分:1)

您需要的是一个线程安全,无阻塞,volatilestatic变量来为您执行计数。

非常感谢,.NET框架提供了管理方式来执行您想要的任务。

对于初学者,您需要一个易失性的静态变量作为计数器。声明它(所有线程都可以访问它):

public static volatile int volatileCounter; 

其中 static 表示这是一个类而不是实例成员, volatile 可防止缓存错误发生。

接下来,您将需要一个以线程安全且无阻塞的方式递增它的代码。如果您不希望您的计数器超出int变量的限制(很可能),您可以使用Interlocked class来表示:

Interlocked.Increment(ref yourInstance.volatileCounter);

interlocked class将保证您的增量操作将原子,因此没有竞争条件可能导致错误结果,并且它也是以非常重的方式进行非阻塞{这里涉及{3}}和线程阻塞。