سؤال

لقد لعب مع مجموعات خيوط وجاء عبر أنيق أساليب الإرشاد خلقت الناس إلى سهولة استخدام ReaderWriterLockSlim بالسماح IDisposable نمط.

ومع ذلك, أعتقد أنني أدركت أن شيئا ما في تنفيذ الأداء القاتل.وأنا أدرك أن امتداد طرق لا يفترض أن تؤثر على الأداء, لذلك أنا تركت افتراض أن شيئا ما في تنفيذ ما هو سبب...المبلغ المتاح البنيات التي تم إنشاؤها/جمعها ؟

هنا بعض اختبار التعليمات البرمجية:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Diagnostics;

namespace LockPlay {

    static class RWLSExtension {
        struct Disposable : IDisposable {
            readonly Action _action;
            public Disposable(Action action) {
                _action = action;
            }
            public void Dispose() {
                _action();
            }
        } // end struct
        public static IDisposable ReadLock(this ReaderWriterLockSlim rwls) {
            rwls.EnterReadLock();
            return new Disposable(rwls.ExitReadLock);
        }
        public static IDisposable UpgradableReadLock(this ReaderWriterLockSlim rwls) {
            rwls.EnterUpgradeableReadLock();
            return new Disposable(rwls.ExitUpgradeableReadLock);
        }
        public static IDisposable WriteLock(this ReaderWriterLockSlim rwls) {
            rwls.EnterWriteLock();
            return new Disposable(rwls.ExitWriteLock);
        }
    } // end class

    class Program {

        class MonitorList<T> : List<T>, IList<T> {
            object _syncLock = new object();
            public MonitorList(IEnumerable<T> collection) : base(collection) { }
            T IList<T>.this[int index] {
                get {
                    lock(_syncLock)
                        return base[index];
                }
                set {
                    lock(_syncLock)
                        base[index] = value;
                }
            }
        } // end class

        class RWLSList<T> : List<T>, IList<T> {
            ReaderWriterLockSlim _rwls = new ReaderWriterLockSlim();
            public RWLSList(IEnumerable<T> collection) : base(collection) { }
            T IList<T>.this[int index] {
                get {
                    try {
                        _rwls.EnterReadLock();
                        return base[index];
                    } finally {
                        _rwls.ExitReadLock();
                    }
                }
                set {
                    try {
                        _rwls.EnterWriteLock();
                        base[index] = value;
                    } finally {
                        _rwls.ExitWriteLock();
                    }
                }
            }
        } // end class

        class RWLSExtList<T> : List<T>, IList<T> {
            ReaderWriterLockSlim _rwls = new ReaderWriterLockSlim();
            public RWLSExtList(IEnumerable<T> collection) : base(collection) { }
            T IList<T>.this[int index] {
                get {
                    using(_rwls.ReadLock())
                        return base[index];
                }
                set {
                    using(_rwls.WriteLock())
                        base[index] = value;
                }
            }
        } // end class

        static void Main(string[] args) {
            const int ITERATIONS = 100;
            const int WORK = 10000;
            const int WRITE_THREADS = 4;
            const int READ_THREADS = WRITE_THREADS * 3;

            // create data - first List is for comparison only... not thread safe
            int[] copy = new int[WORK];
            IList<int>[] l = { new List<int>(copy), new MonitorList<int>(copy), new RWLSList<int>(copy), new RWLSExtList<int>(copy) };

            // test each list
            Thread[] writeThreads = new Thread[WRITE_THREADS];
            Thread[] readThreads = new Thread[READ_THREADS];
            foreach(var list in l) {
                Stopwatch sw = Stopwatch.StartNew();
                for(int k=0; k < ITERATIONS; k++) {
                    for(int i = 0; i < writeThreads.Length; i++) {
                        writeThreads[i] = new Thread(p => {
                            IList<int> il = p as IList<int>;
                            int c = il.Count;
                            for(int j = 0; j < c; j++) {
                                il[j] = j;
                            }
                        });
                        writeThreads[i].Start(list);
                    }
                    for(int i = 0; i < readThreads.Length; i++) {
                        readThreads[i] = new Thread(p => {
                            IList<int> il = p as IList<int>;
                            int c = il.Count;
                            for(int j = 0; j < c; j++) {
                                int temp = il[j];
                            }
                        });
                        readThreads[i].Start(list);
                    }
                    for(int i = 0; i < readThreads.Length; i++)
                        readThreads[i].Join();
                    for(int i = 0; i < writeThreads.Length; i++)
                        writeThreads[i].Join();
                };
                sw.Stop();
                Console.WriteLine("time: {0} class: {1}", sw.Elapsed, list.GetType());
            }
            Console.WriteLine("DONE");
            Console.ReadLine();
        }
    } // end class
} // end namespace

هنا نتيجة نموذجية:

time: 00:00:03.0965242 class: System.Collections.Generic.List`1[System.Int32]
time: 00:00:11.9194573 class: LockPlay.Program+MonitorList`1[System.Int32]
time: 00:00:08.9510258 class: LockPlay.Program+RWLSList`1[System.Int32]
time: 00:00:16.9888435 class: LockPlay.Program+RWLSExtList`1[System.Int32]
DONE

كما يمكنك أن ترى باستخدام امتداد في الواقع يجعل الأداء أسوأ من مجرد استخدام lock (رصد).

هل كانت مفيدة؟

المحلول

يبدو مثل سعر إنشاء الملايين من البنيات و بت إضافية من الدعاء.

وأود أن أذهب إلى حد القول أن ReaderWriterLockSlim يساء استخدامها في هذه العينة قفل جيدة بما يكفي في هذه الحالة وأداء حافة تحصل مع ReaderWriterLockSlim لا يكاد يذكر بالمقارنة مع سعر شرح هذه المفاهيم إلى صغار المشروعات الإنمائية.

يمكنك الحصول على ضخمة ميزة مع القارئ الكاتب أسلوب أقفال عندما يأخذ غير negligable قدر من الوقت لأداء يقرأ ويكتب.دفعة سوف يكون أكبر عندما يكون لديك في الغالب قراءة القائمة على النظام.

حاول إدراج الموضوع.النوم(1) في حين الأقفال يتم الحصول على لنرى كيف ضخمة يجعل الفرق.

ترى هذا المعيار:

Time for Test.SynchronizedList`1[System.Int32] Time Elapsed 12310 ms
Time for Test.ReaderWriterLockedList`1[System.Int32] Time Elapsed 547 ms
Time for Test.ManualReaderWriterLockedList`1[System.Int32] Time Elapsed 566 ms

في القياس لا تلاحظ الكثير من فرق بين اثنين من الأساليب ، أشعر مريحة استخدامه شريطة أن بعض finalizer الحماية في حالة الناس تنسى أن تخلص ....

using System.Threading;
using System.Diagnostics;
using System.Collections.Generic;
using System;
using System.Linq;

namespace Test {

    static class RWLSExtension {
        struct Disposable : IDisposable {
            readonly Action _action;
            public Disposable(Action action) {
                _action = action;
            }
            public void Dispose() {
                _action();
            }
        }

        public static IDisposable ReadLock(this ReaderWriterLockSlim rwls) {
            rwls.EnterReadLock();
            return new Disposable(rwls.ExitReadLock);
        }
        public static IDisposable UpgradableReadLock(this ReaderWriterLockSlim rwls) {
            rwls.EnterUpgradeableReadLock();
            return new Disposable(rwls.ExitUpgradeableReadLock);
        }
        public static IDisposable WriteLock(this ReaderWriterLockSlim rwls) {
            rwls.EnterWriteLock();
            return new Disposable(rwls.ExitWriteLock);
        }
    }

    class SlowList<T> {

        List<T> baseList = new List<T>();

        public void AddRange(IEnumerable<T> items) {
            baseList.AddRange(items);
        }

        public virtual T this[int index] {
            get {
                Thread.Sleep(1);
                return baseList[index];
            }
            set {
                baseList[index] = value;
                Thread.Sleep(1);
            }
        }
    }

    class SynchronizedList<T> : SlowList<T> {

        object sync = new object();

        public override T this[int index] {
            get {
                lock (sync) {
                    return base[index];
                }

            }
            set {
                lock (sync) {
                    base[index] = value;
                }
            }
        }
    }


    class ManualReaderWriterLockedList<T> : SlowList<T> {

        ReaderWriterLockSlim slimLock = new ReaderWriterLockSlim();

        public override T this[int index] {
            get {
                T item;
                try {
                    slimLock.EnterReadLock();
                    item = base[index];
                } finally {
                    slimLock.ExitReadLock();
                }
                return item;

            }
            set {
                try {
                    slimLock.EnterWriteLock();
                    base[index] = value;
                } finally {
                    slimLock.ExitWriteLock();
                }
            }
        }
    }

    class ReaderWriterLockedList<T> : SlowList<T> {

        ReaderWriterLockSlim slimLock = new ReaderWriterLockSlim();

        public override T this[int index] {
            get {
                using (slimLock.ReadLock()) {
                    return base[index];
                }
            }
            set {
                using (slimLock.WriteLock()) {
                    base[index] = value;
                }
            }
        }
    }


    class Program {


        private static void Repeat(int times, int asyncThreads, Action action) {
            if (asyncThreads > 0) {

                var threads = new List<Thread>();

                for (int i = 0; i < asyncThreads; i++) {

                    int iterations = times / asyncThreads;
                    if (i == 0) {
                        iterations += times % asyncThreads;
                    }

                    Thread thread = new Thread(new ThreadStart(() => Repeat(iterations, 0, action)));
                    thread.Start();
                    threads.Add(thread);
                }

                foreach (var thread in threads) {
                    thread.Join();
                }

            } else {
                for (int i = 0; i < times; i++) {
                    action();
                }
            }
        }

        static void TimeAction(string description, Action func) {
            var watch = new Stopwatch();
            watch.Start();
            func();
            watch.Stop();
            Console.Write(description);
            Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds);
        }

        static void Main(string[] args) {

            int threadCount = 40;
            int iterations = 200;
            int readToWriteRatio = 60;

            var baseList = Enumerable.Range(0, 10000).ToList();

            List<SlowList<int>> lists = new List<SlowList<int>>() {
                new SynchronizedList<int>() ,
                new ReaderWriterLockedList<int>(),
                new ManualReaderWriterLockedList<int>()
            };

            foreach (var list in lists) {
                list.AddRange(baseList);
            }


            foreach (var list in lists) {
                TimeAction("Time for " + list.GetType().ToString(), () =>
                {
                    Repeat(iterations, threadCount, () =>
                    {
                        list[100] = 99;
                        for (int i = 0; i < readToWriteRatio; i++) {
                            int ignore = list[i];
                        }
                    });
                });
            }



            Console.WriteLine("DONE");
            Console.ReadLine();
        }
    }
}

نصائح أخرى

رمز يظهر على استخدام البنية لتجنب إنشاء كائن النفقات العامة ، ولكن لا تأخذ الخطوات الأخرى الضرورية للحفاظ على هذا الوزن.وأعتقد أنه المربعات قيمة الإرجاع من ReadLock, و إذا كان الأمر كذلك ينفي كامل الاستفادة من البنية.وهذا ينبغي أن إصلاح جميع القضايا وتنفيذ فقط فضلا عن عدم الذهاب من خلال IDisposable واجهة.

تحرير:المعايير المطلوبة.هذه النتائج هي طبيعية لذلك دليل طريقة (دعوة Enter/ExitReadLock و Enter/ExitWriteLock مضمنة مع المحمية رمز) لديها قيمة الوقت من 1.00. الأسلوب الأصلي بطيئة لأنه تخصص الكائنات على كومة من أن الطريقة اليدوية لا.أنا إصلاح هذه المشكلة في الإصدار الوضع حتى طريقة تمديد دعوة علوية يذهب بعيدا وترك مماثل بسرعة كما الأسلوب اليدوي.

Debug:

Manual:              1.00
Original Extensions: 1.62
My Extensions:       1.24

بناء الإصدار:

Manual:              1.00
Original Extensions: 1.51
My Extensions:       1.00

قانون بلدي:

internal static class RWLSExtension
{
    public static ReadLockHelper ReadLock(this ReaderWriterLockSlim readerWriterLock)
    {
        return new ReadLockHelper(readerWriterLock);
    }

    public static UpgradeableReadLockHelper UpgradableReadLock(this ReaderWriterLockSlim readerWriterLock)
    {
        return new UpgradeableReadLockHelper(readerWriterLock);
    }

    public static WriteLockHelper WriteLock(this ReaderWriterLockSlim readerWriterLock)
    {
        return new WriteLockHelper(readerWriterLock);
    }

    public struct ReadLockHelper : IDisposable
    {
        private readonly ReaderWriterLockSlim readerWriterLock;

        public ReadLockHelper(ReaderWriterLockSlim readerWriterLock)
        {
            readerWriterLock.EnterReadLock();
            this.readerWriterLock = readerWriterLock;
        }

        public void Dispose()
        {
            this.readerWriterLock.ExitReadLock();
        }
    }

    public struct UpgradeableReadLockHelper : IDisposable
    {
        private readonly ReaderWriterLockSlim readerWriterLock;

        public UpgradeableReadLockHelper(ReaderWriterLockSlim readerWriterLock)
        {
            readerWriterLock.EnterUpgradeableReadLock();
            this.readerWriterLock = readerWriterLock;
        }

        public void Dispose()
        {
            this.readerWriterLock.ExitUpgradeableReadLock();
        }
    }

    public struct WriteLockHelper : IDisposable
    {
        private readonly ReaderWriterLockSlim readerWriterLock;

        public WriteLockHelper(ReaderWriterLockSlim readerWriterLock)
        {
            readerWriterLock.EnterWriteLock();
            this.readerWriterLock = readerWriterLock;
        }

        public void Dispose()
        {
            this.readerWriterLock.ExitWriteLock();
        }
    }
}

تخميني (كنت في حاجة إلى ملف للتحقق من) هو أن انخفاض أداء ليس من خلق المتاح الحالات (ينبغي أن تكون رخيصة إلى حد ما ، يجري البنيات).بدلا أتوقع إنها من خلق عمل المندوبين.قد تتمكن من محاولة تغيير تنفيذ المتاح البنية تخزين مثيل ReaderWriterLockSlim بدلا من إنشاء عمل مندوب.

تحرير: @280Z28 آخر يؤكد أنه تخصيص كتلة العمل الوفود التي تسبب التباطؤ.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top