Композитные замки с использованием LINQ. Кто-нибудь может увидеть какие-либо проблемы с этим? - PullRequest
1 голос
/ 16 февраля 2011

Я играл с 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   
}



1 Ответ

0 голосов
/ 17 февраля 2011

Несколько простых замечаний от меня:

1) Не компилируется, я что-то упустил?Конкретный C # превью или что?

2) Вы используете один объект syncObject, который станет основным узким местом.По сути, вы превращаете все это обратно в однопоточное приложение.Есть более простые способы сделать это.Этот автоматический параллелизм достигается при значительных потерях производительности.

3) Здесь есть только одно поле, которое может быть повреждено, почему бы не заблокировать его средства доступа?Вы все еще можете составлять операции над ними.Правда, дизайнер классов должен был бы принять во внимание блокировку и параллелизм.Но с другой стороны, используя ваш подход, программист должен понимать ваши составные классы.Я бы пошел с нормальной блокировкой, потому что большинство людей уже знают это.

Это интересная мысль, но я пока не вижу смысла в этом, и есть некоторые недостатки.Но есть много мыслей о транзакционной памяти и т. Д. Это может привести к чему-то.

ГДж

...