1

私は LINQ をいじっていて、C# Monadic 構文を利用してロックを作成するための次のアイデアを思いつきました。単純すぎるように思えるので、StackOverflow に投稿して、このアプローチの大きな問題を誰かがすぐに見つけられるかどうかを確認してみようと思いました。

アトミック実装内のロックにコメントすると、アカウントが破損していることがわかります。

このアイデアは、明示的なロックなしでアカウントの操作を構成できるようにすることです。

using System;
using System.Collections.Generic;
using System.Linq;

namespace AtomicLinq
{
    public static class AccountCombinators
    {
        public static IAtomic <Unit> TransferAndReverse(this Account accA, 
                                                      Account accB, 
                                                      double amount)
        {
            return from a in accA.TransferTo(accB, amount)
                   from b in accB.TransferTo(accA, amount)
                   select new Unit();
        }

        public static IAtomic<Unit> TransferTo(this Account accA, 
                                              Account accB, 
                                              double amount)
        {
            return from a in accA.Withdraw(amount)
                   from b in accB.Deposit(amount)
                   select new Unit();
        }

        public static IAtomic<double> Withdraw(this Account acc, double amount)
        {
            return Atomic.Create(() => acc.Amount -= amount);
        }

        public static IAtomic<double> Deposit(this Account acc, double amount)
        {
            return Atomic.Create(() => acc.Amount += amount);
        }
    }

    static class Program
    {
        static void Main(string[] args)
        {
            var accA = new Account("John") { Amount = 100.0 };
            var accB = new Account("Mark") { Amount = 200.0 };

            var syncObject = new object();
            Enumerable.Range(1, 100000).AsParallel().Select(_ => accA.TransferAndReverse(accB, 100).Execute(syncObject)).Run();

            Console.WriteLine("{0} {1}", accA, accA.Amount);
            Console.WriteLine("{0} {1}", accB, accB.Amount);

            Console.ReadLine();
        }        
    }    

    public class Account
    {
        public double Amount { get; set; }
        private readonly string _name;

        public Account(string name)
        {
            _name = name;
        }

        public override string ToString()
        {
            return _name;
        }
    }

    #region Atomic Implementation

    public interface IAtomic<T>
    {
        T Execute(object sync);
    }

    public static class Atomic
    {
        public static IAtomic<T> Create<T>(Func<object, T> f)
        {
            return new AnonymousAtomic<T>(f);
        }

        public static IAtomic<T> Create<T>(Func<T> f)
        {
            return Create(_ => f());
        }

        public static IAtomic<T> Aggregate<T>(this IEnumerable<IAtomic<T>> xs)
        {
            return xs.Aggregate((x, y) => from a in x
                                          from b in y
                                          select b);
        }

        public static IAtomic<K> SelectMany<T, V, K>(this IAtomic<T> m, Func<T, IAtomic<V>> f, Func<T, V, K> p)
        {
            return Create(sync =>
                              {
                                  var t = m.Execute(sync);
                                  var x = f(t);
                                  return p(t, x.Execute(sync));
                              });
        }
    }

    public class AnonymousAtomic<T> : IAtomic<T>
    {
        private readonly Func<object, T> _func;

        public AnonymousAtomic(Func<object, T> func)
        {
            _func = func;
        }

        public T Execute(object sync)
        {
            lock (sync)  // Try to comment this lock you'll see that the accounts get corrupted
                return _func(sync);
        }
    }

    #endregion Atomic Implementation   
}



4

1 に答える 1

0

私からのいくつかの簡単なポイント:

1) コンパイルされません。何か不足していますか? 特定の C# プレビューか何か?

2) 大きなボトルネックとなる 1 つの syncObject を使用しています。基本的に、すべてを単一のスレッド化されたアプリケーションに戻します。それを行う簡単な方法があります。この自動的な並行性は、かなりのパフォーマンス コストを伴います。

3) 破損する可能性のあるフィールドは 1 つだけです。そのアクセサーをロックしてみませんか? それらに対する操作を構成することもできます。確かに、クラスの設計者はロックと並行性に注意する必要があります。しかし一方で、あなたのアプローチを使用すると、プログラマーはあなたの構成クラスを理解する必要があります。ほとんどの人はすでにそれを知っているので、通常のロックを使用します。

興味深い考えですが、この方法の利点はまだわかりませんし、欠点もいくつかあります。しかし、トランザクショナル メモリなどについて多くのことを考えています。これは何かにつながる可能性があります。

GJ

于 2011-02-16T21:38:20.923 に答える