Компонуемыми замки с помощью LINQ. Может кто видит в этом проблему ?


Я играл вокруг с LINQ и я придумал следующую идею для создания замков воспользовавшись синтаксис c# монад. Это кажется слишком простым, поэтому я думал, позвольте мне разместить его на клиент StackExchange и посмотреть, если любой желающий может быстро обнаружить любой крупной проблемы с этим подходом.

Если вы прокомментируете замок в атомном выполнении вы увидите учетные записи поврежденным.

Эта идея о возможности сочинять операций на счетах без явной блокировки.

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   
}





584
2
задан 16 февраля 2011 в 09:02 Источник Поделиться
Комментарии
2 ответа

Недостаток я вижу в вашей конструкции является то, что вы отделены замок от реальных объектов, которые не должны быть изменены другим потоком во время критической секции.
В вашем случае только два счета сделки должны быть закрыты, но вы создали глобальную блокировку syncObject , что практически превращает ваш параллельного выполнения в серийное. Другими словами, если у вас ассс и ОРКК в Главном не могли бы вы сделать перевод между ассс и ОРКК параллельно с переносом между Асса и accB.
Вы могли бы быть в состоянии взломать это путем создания серии блокировка объекты и передать их как-то по одному в правильном порядке, чтобы выполнить , но если позвонить в параллель выполнение с гораздо более высокого уровня абстракции, то манипулировать объектами на вы, вероятно, не сможет пройти надлежащее блокировка объектов для выполнения. Единственным вариантом могло бы быть использование глобальной блокировки для этого методами asparallel() "сессии" и это будет противоречить цели его использования.

1
ответ дан 18 февраля 2011 в 10:02 Источник Поделиться

Я пытаюсь скомпилировать код:


  • Какого класса аппарат? Нужно ли мне ссылаться на дополнительную сборку?

  • Запустить в конце перечисли.Ассортимент(1, 100000).Методами asparallel().Выберите(_ => Асса.TransferAndReverse(accB, 100).Выполнить(syncObject)).Выполнить(); по-видимому, излишне.

0
ответ дан 17 февраля 2011 в 11:02 Источник Поделиться