Fórum Root.cz

Hlavní témata => Vývoj => Téma založeno: anonym 28. 01. 2018, 18:01:58

Název: Synchronizace vlaken v C#
Přispěvatel: anonym 28. 01. 2018, 18:01:58
Mám takový problém. Dal bych krk na to, že v Javě Semafor a Mutex nerozlišuje, jaké vlákno si zámek vzalo. C#, ta bestie, mi dělá tohle:

Mutex m = new Mutex();
m.WaitOne();
m.WaitOne(); <<< tady by se melo vlakno zastavit ale nezastavi se, protože si ten vyčůraný mutex pamatuje, že je to stejné vlákno

Jenže, já potřebuju Mutex a Semafor klasický, něco co má metodu Acquire() a nestará se, jaké vlákno to chce. A pokud nemůže získat zámek, bude prostě čekat. Jak na to pls?
Název: Re:Synchronizace vlaken v C#
Přispěvatel: Filip Jirsák 28. 01. 2018, 19:19:50
V Javě i v jakémkoli jiném alespoň trochu příčetném frameworku je vlastníkem zámku pro synchronizaci vláken vždy vlákno, takže když se pokusí získat zámek, který už vlastní, dostane ho. Žádné jiné řešení totiž nedává smysl – kdyby se to chovalo tak, jak byste chtěl, na tom třetím řádku dojde k deadlocku a to vlákno už se nikdy neprobudí, protože by čekalo na uvolnění zámku, k jehož uvolnění by ale bylo potřeba, aby to vlákno běželo.

Takže evidentně potřebujete něco jiného. Zkuste raději popsat problém, který řešíte.
Název: Re:Synchronizace vlaken v C#
Přispěvatel: Neviditelný 28. 01. 2018, 19:35:36
To, co popisujete je chování rekurzivních mutexů, které jsou v microsoftích frameworkách běžné. Kromě toho, že rozumně navržený kód by rekurzivní synchronizační primitiva neměl potřebovat mi není jasné, proč chcete z jednoho vlákna zamykat mutex dvakrát a při tom druhém zamknutí čekat. Jakým způsobem zařídíte, že vám to vlákno nevydeadlockuje?
Název: Re:Synchronizace vlaken v C#
Přispěvatel: Filip Jirsák 28. 01. 2018, 20:42:53
Předpokládám,že nechce synchronizovat vlákna, ale něco jiného, takže to odemkne z jiného vlákna. Ale co doopravdy chce, to musí napsat anonymní tazatel, já jenom hádám.
Název: Re:Synchronizace vlaken v C#
Přispěvatel: anonym 28. 01. 2018, 21:33:03
Ok našel jsem, jmenuje se to AutoResetEvent.

Potřeboval jsem udělat toto:

IEnumerable<T> ThreadedPipe.IterateInThread(IEnumerable<T> input)

Tzn. input, ktery bych normalne prochazel primo pres foreach, budu prochazet pres tuto metodu, a ta metoda spustí načítání inputu v samostatném threadu. Můžu si takto paralelizovat proudové zpracování. Neříká se tomu Pipe and Filters? Tak tohle co jsem udělal je Pipe, Filtr na který to napojím líně načítá XMLko z disku.

Pokud má někdo lepší řešení, tak sem s nim, já se u toho pěkně zapotil a stejně mi to trochu hapruje.
Název: Re:Synchronizace vlaken v C#
Přispěvatel: anonym 28. 01. 2018, 21:35:06
Ještě tu připojuju zdroják, kdyby někdo měl chuť se podívat na mé řešení  ???

Kód: [Vybrat]
public class ThreadedPipe<T>
    {
        Queue<T> buffer = new Queue<T>();
        int bufferSize = 100;

        AutoResetEvent taskFinished = new AutoResetEvent(false);
        AutoResetEvent taskEnqueued = new AutoResetEvent(false);
        AutoResetEvent[] taskEnqueuedOrFinished = new AutoResetEvent[2];

        AutoResetEvent readyEnqueue = new AutoResetEvent(false);


        public ThreadedPipe(int bufferSize)
        {
            taskEnqueuedOrFinished[0] = taskFinished;
            taskEnqueuedOrFinished[1] = taskEnqueued;

            this.bufferSize = bufferSize;
        }

        private void Enqueue(T item)
        {
            lock(buffer)
            {
                buffer.Enqueue(item);
            }
        }
        private T Dequeue()
        {
            lock (buffer)
            {
                return buffer.Dequeue();
            }
        }

        private int Count()
        {
            lock (buffer)
            {
                return buffer.Count;
            }
        }
        public IEnumerable<T> IterateInThread(IEnumerable<T> input)
        {
            buffer.Clear();

            Task t = new Task(() =>
            {
                foreach (var entry in input)
                {
                    if (Count() >= bufferSize)
                    {
                        readyEnqueue.WaitOne();
                    }

                    Enqueue(entry);
                    taskEnqueued.Set();
                    Trace.WriteLine("Enqueue. Count:" + Count());
                }
                taskFinished.Set();
            });
            t.Start();

            while (true)
            {
                if(Count()>0)
                {
                    yield return Dequeue();
                    readyEnqueue.Set();
                }else
                {
                    int num = WaitHandle.WaitAny(taskEnqueuedOrFinished);
                   
                    if(num == 0) // Task finished
                    {
                        break;
                    }
                }
            }
        }

    }
Název: Re:Synchronizace vlaken v C#
Přispěvatel: Neviditelný 28. 01. 2018, 22:00:59
Chápu správně, že jde o problém producent-konzument kdy producent čte nějaká data a láduje je do bufferu, odkud si je konzument sebere? Pokud je to tak, nešlo by to vyřešit semaforem?
Název: Re:Synchronizace vlaken v C#
Přispěvatel: anonym 28. 01. 2018, 22:07:26
To uplne neni producent/consumer

(http://androidsrc.net/wp-content/uploads/2015/02/PCP_AndroidSRC.net_.png)

Spíše je to Pipe and Filters

(http://www.rantdriven.com/image.axd?picture=pipe-and-filters-concept.png)

Název: Re:Synchronizace vlaken v C#
Přispěvatel: anonym 28. 01. 2018, 22:11:59
Hm ono to je asi jedno... jo je to producent/konzumer. Ale semafor mi bude nanic.
Název: Re:Synchronizace vlaken v C#
Přispěvatel: Neviditelný 28. 01. 2018, 22:22:56
Hm ono to je asi jedno... jo je to producent/konzumer. Ale semafor mi bude nanic.
Proč myslíte? Mám dojem, že třeba příklad na použití semaforů v Qt (https://doc.qt.io/qt-5/qtcore-threads-semaphores-example.html) řeší hodně podobný, ne-li identický problém a nemusí zamykat celý buffer.
Název: Re:Synchronizace vlaken v C#
Přispěvatel: anonym 28. 01. 2018, 22:26:45
Haha už jsem to našel!

https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/blockingcollection-overview

Jde se refaktorovat.
Název: Re:Synchronizace vlaken v C#
Přispěvatel: kvr kvr kvr 29. 01. 2018, 00:20:49
Pominu-li race condition v každém druhém bloku, což by asi bylo lépe vidět, kdy by třída měla dokončené finální konzumenty, tak... v Java by na to byl ideální Executor, s limitovaným počtem threadů, které můžou paralelně procesovat. Předpokládám, že C# má něco podobného - namátkou ThreadPool....? BlockingQueue je struktura na nižší úrovni, která se dá pro Executor využít.
Název: Re:Synchronizace vlaken v C#
Přispěvatel: jpu 29. 01. 2018, 07:26:11
pokial ti ide o pattern producent/konzument, tak tam je najlepsie pouzit DataFlow. Tu je aj navod od MS:
https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/how-to-implement-a-producer-consumer-dataflow-pattern (https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/how-to-implement-a-producer-consumer-dataflow-pattern)