myCSharp.de - DIE C# und .NET Community
Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 
 | Suche | FAQ

» Hauptmenü
myCSharp.de
» Startseite
» Forum
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Suche
» Regeln
» Wie poste ich richtig?
» Forum-FAQ

Mitglieder
» Liste / Suche
» Wer ist wo online?

Ressourcen
» openbook: Visual C#
» openbook: OO
» Microsoft Docs

Team
» Kontakt
» Übersicht
» Wir über uns

» myCSharp.de Diskussionsforum
Du befindest Dich hier: Community-Index » Diskussionsforum » Entwicklung » Basistechnologien und allgemeine .NET-Klassen » Threadsafe Liste, bei der ein Tasks den exklusiven Zugriff erhalten kann
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | Thema zu Favoriten hinzufügen

Antwort erstellen
Zum Ende der Seite springen  

Threadsafe Liste, bei der ein Tasks den exklusiven Zugriff erhalten kann

 
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
Palladin007 Palladin007 ist männlich
myCSharp.de-Mitglied

Dabei seit: 03.02.2012
Beiträge: 1.199
Entwicklungsumgebung: Visual Studio 2017
Herkunft: NRW


Palladin007 ist offline

Threadsafe Liste, bei der ein Tasks den exklusiven Zugriff erhalten kann

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

'n Abend,

ich brauche eine Liste, die von x-beliebig vielen Threads genutzt werden kann. Soweit so gut, mein Problem ist, dass ein Task (oder mehrere) auch exklusiven Zugriff bekommen können soll. Der jeweilige Task, der den exklusiven Zugriff hat, darf beliebig lesen und schreiben, während alle Threads warten müssen, bis der Task den allgemeinen Zugriff wieder frei gibt.

Oder anderes Formuliert:
Viele Threads lesen und schreiben fleißig in einer Liste, dabei wird nie für einen längeren Zeitraum gelockt.
Parallel dazu kann ein Task (also theoretisch mehrere Threads) ebenfalls beliebig lesen oder schreiben, bis er besagten exklusiven Zugriff beantragt. Er wartet dann so lange, bis alle laufenden Aktionen oder ein anderer exklusiver Zugriff beendet sind und bekommt dann z.B. ein Handle-Objekt, mit dem er weiter arbeiten kann.

Gibt es für solche Vorhaben schon vorhandene Klassen in .NET, die das entweder können, oder vereinfachen?
Oder gibt es allgemeine Konzepte, an denen ich mich bei der Umsetzung einer eigenen Lösung orientieren kann?
Oder habe ich etwas total einfaches übersehen? :D

Beste Grüße
23.08.2019 00:23 Beiträge des Benutzers | zu Buddylist hinzufügen
Spook Spook ist männlich
myCSharp.de-Mitglied

Dabei seit: 28.10.2008
Beiträge: 201
Entwicklungsumgebung: VS2015 Professional
Herkunft: Esslingen a.N.


Spook ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo Palladin,

für mich klingt das nach   ReaderWriterLock Class.
Mit dieser konnen mehrere Threads parallel aus der Liste lesen, aber immer nur ein Thread die Liste verändern.

Grüße
spooky
23.08.2019 09:20 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Palladin007 Palladin007 ist männlich
myCSharp.de-Mitglied

Dabei seit: 03.02.2012
Beiträge: 1.199
Entwicklungsumgebung: Visual Studio 2017
Herkunft: NRW

Themenstarter Thema begonnen von Palladin007

Palladin007 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Die Klasse scheint zu tun, was ich brauche, allerdings mit einem ganz entscheidenden Nachteil:
Wechselt der Thread, hab ich einen Deadlock...

Ich arbeite mit Tasks und ein simples "await Task.Delay(1000)" kann dafür sorgen, dass der darauf folgende Code in einem anderen Thread läuft und dann auf immer und ewig darauf wartet, etwas tun zu dürfen.
24.08.2019 14:37 Beiträge des Benutzers | zu Buddylist hinzufügen
FZelle
myCSharp.de-Poweruser/ Experte

Dabei seit: 23.04.2004
Beiträge: 9.804


FZelle ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Was ist mit der  SynchronizedCollection
24.08.2019 16:04 Beiträge des Benutzers | zu Buddylist hinzufügen
Palladin007 Palladin007 ist männlich
myCSharp.de-Mitglied

Dabei seit: 03.02.2012
Beiträge: 1.199
Entwicklungsumgebung: Visual Studio 2017
Herkunft: NRW

Themenstarter Thema begonnen von Palladin007

Palladin007 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Die synchronisiert ja nur die Zugriffe zwischen den Threads, ich kann aber nicht alle Threads aussperren und nur den Zugriff von einem Tasks aus erlauben.

Diese Liste bietet zwar das SyncRoot-Objekt nach draußen an, allerdings kann ich das nur begrenzt nutzen.

Im Grunde suche ich sowas:

C#-Code:
// Thread 1 bis X:
myList.Add(123);
// oder:
lock(list.SyncRoot)
{
    var item = myList.Count + 1;

    myList.Add(count);
}

// Task:
lock (list.SyncRoot)
{
    for (var i = 0; i < 10; i++)
    {
       await Task.Yield(); // <-- Nur zum simulieren verschiedener Threads

       var item = myList.Count + i;

       myList.Add(count);
   }
}

Das funktioniert aber nicht, ein await kann ich (aus gutem Grund) nicht in einem lock nutzen.

Ich suche also sozusagen eine Art Schlüssel, der sämtliche Zugriffe sperrt, außer wenn man bei diesem Zugriff den Schlüssel mit gibt.

Meine Ideal-Vorstellung wäre sowas wie:

C#-Code:
// Task:
using (var list2 = list.TakeLock())
{
    for (var i = 0; i < 10; i++)
    {
       await Task.Yield(); // <-- Nur zum simulieren verschiedener Threads

       var item = list2 .Count + i;

       list2 .Add(count);
   }
}

TakeLock() gibt einen Wrapper zurück, der den Zugriff regelt und beim Aufruf von Dispose() die Sperre wieder frei gibt.
24.08.2019 16:19 Beiträge des Benutzers | zu Buddylist hinzufügen
Spook Spook ist männlich
myCSharp.de-Mitglied

Dabei seit: 28.10.2008
Beiträge: 201
Entwicklungsumgebung: VS2015 Professional
Herkunft: Esslingen a.N.


Spook ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Also geht es dir primär nicht um das Synchronisieren eines konkreten Zugriffs auf eine Instanz von List<T> (oder ähnlich) sondern du möchtest eher zwei getrennte Zugriffsebenen?

Du könntest mehrere ReaderWriterLocks oder Sync-Objekte verwenden. Die "normalen" Tasks locken immer nur Ebene 1. Wenn ein Task exklusiven Zugriff möchte lockt er ebenfalls Ebene 1 und die Threads dieses Tasks locken nur Ebene 2 um sich intern zu synchronisieren.
24.08.2019 18:48 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Papst Papst ist männlich
myCSharp.de-Mitglied

Dabei seit: 28.09.2014
Beiträge: 218
Entwicklungsumgebung: VS2017
Herkunft: Kassel


Papst ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Lässt sich das nicht gut it einer  SemaphoreSlim lösen?

-> Initialisiere die Semaphore mit einem Counter, der deiner Anzahl threads enspricht.
-> Jeder Thread der schreiben will holt sich eine Semaphore und gibt sie nach jeder Operation wieder frei
-> Will ein Thread exklusiv Zugriff, holt er sich einfach alle
24.08.2019 19:02 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Palladin007 Palladin007 ist männlich
myCSharp.de-Mitglied

Dabei seit: 03.02.2012
Beiträge: 1.199
Entwicklungsumgebung: Visual Studio 2017
Herkunft: NRW

Themenstarter Thema begonnen von Palladin007

Palladin007 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Das Problem bei den genannten Verfahren (ReaderWriterLock, Monitor bzw. lock, Semaphore/Slim) ist, dass die alle nur so lange funktionieren, wie der Thread noch der Selbe ist, wie zum Zeitpunkt, als der Zugriff beantragt wurde.
Bei Tasks muss ich aber damit rechnen, dass ich mehrere Threads habe und die fröhlich hin und her wechseln, ohne dass ich das voraus ahnen kann.

Ich denke daher nicht, dass es ohne z.B. einer Art Lock-Handle-Objekt funktioniert, also dass der Zugriff nur erlaubt wird, wenn eben dieses Handle-Objekt mit gegeben wird. Man sperrt diesen Key und jeder andere Zugriff mit dem selben Key muss warten, bis der Key wieder frei wird.
24.08.2019 20:34 Beiträge des Benutzers | zu Buddylist hinzufügen
T-Virus T-Virus ist männlich
myCSharp.de-Mitglied

Dabei seit: 17.04.2008
Beiträge: 1.320
Entwicklungsumgebung: Visual Studio, Codeblocks, Edi
Herkunft: Nordhausen, Nörten-Hardenberg


T-Virus ist offline Füge T-Virus Deiner Kontaktliste hinzu

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

@Palladin007
Warum hast du den mit den Tasks und den ConcurrentCollections deine Probleme?
Ein Task, der z.B. mit Task.Run gestartet wird, hat seinen eigenen Thread weshalb dies kein Problem sein kann.
Nur wenn du z.B. über Task.Factory.StartNew arbeitest, kann es sein, dass dieser erst im aktuellen und einige Zeit später in einem eigenen Thread läuft.

Mir wäre es aber neu, dass die ConcurrentCollections sich nicht mit den Tasks vertragen.

Nachtrag:
Versuchs mal mit der ConcurrentBag<T>

 Doku

T-Virus

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von T-Virus am 24.08.2019 21:01.

24.08.2019 20:59 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Palladin007 Palladin007 ist männlich
myCSharp.de-Mitglied

Dabei seit: 03.02.2012
Beiträge: 1.199
Entwicklungsumgebung: Visual Studio 2017
Herkunft: NRW

Themenstarter Thema begonnen von Palladin007

Palladin007 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Führe folgenden Code ein paar Mal aus:

C#-Code:
static async Task Test()
{
    Console.WriteLine(Environment.CurrentManagedThreadId);
    await Task.Run(() => Thread.Sleep(10));
    Console.WriteLine(Environment.CurrentManagedThreadId);
    await Task.Factory.StartNew(() => Thread.Sleep(10));
    Console.WriteLine(Environment.CurrentManagedThreadId);
    await Task.Delay(10);
    Console.WriteLine(Environment.CurrentManagedThreadId);
    await Task.Yield();
    Console.WriteLine(Environment.CurrentManagedThreadId);
}

Die Ausgabe sieht immer anders aus, z.B.:

Zitat:
14557
14555
14446
14444
14466
14556
14564

Das Problem dabei ist, dass ich nie weiß, was bei einem await passiert. Wie arbeitet z.B. ein EFCore bei einem ToListAsync? Wie arbeitet jedes x-beliebige Framework, das asynchrone Zugriffe erlaubt, habe ich danach noch den alten Thread?

Wenn ich asynchrone Zugriffe erlauben möchte, muss ich damit rechnen, dass der Code nicht mehr im selben Thread läuft, wie vor ein paar Zeilen.

Ich hab also nichts gegen die genannten Klassen wie Semaphore/Slim oder ReaderWriterLock/Slim, aber die gehen alle davon aus, dass der Thread zwischen Begin und Ende des gelockten Codes sich nicht mehr ändert.
Klassen wie ConcurrentBag oder ConcurrentCollections synchronisieren nur den Aufruf einer einzelnen Methode, aber sobald ich den Zugriff über mehrere Aufrufe hinweg sperren möchte, versagen sie.
24.08.2019 21:21 Beiträge des Benutzers | zu Buddylist hinzufügen
witte
myCSharp.de-Mitglied

Dabei seit: 03.09.2010
Beiträge: 823


witte ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

* ein SemaphoreSlim verlangt nicht dass der freigebende Thread derselbe wie der sperrende Thread ist.
* was ist mit ConfigureAwait?
24.08.2019 21:51 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
T-Virus T-Virus ist männlich
myCSharp.de-Mitglied

Dabei seit: 17.04.2008
Beiträge: 1.320
Entwicklungsumgebung: Visual Studio, Codeblocks, Edi
Herkunft: Nordhausen, Nörten-Hardenberg


T-Virus ist offline Füge T-Virus Deiner Kontaktliste hinzu

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

@Palladin007
Bei Methoden wie ToListAsync wird häufig der ToList Aufruf nur per Task.Run gewrappt.
Es gibt aber auch Fälle, wo eben die gesamte Verarbeitung bei Async Methoden neu implementiert werden muss.
Hängt also von der jeweiligen Implementierung ab!

Dein Fall dürfte von .NET nicht abgedeckt sein bzw. wäre mir solch eine Collection nicht bekannt.
Ich würde vermutlich eine Wrapper Klasse bauen, die deine expliziten Zugriffe dann über ein eigenes Lock System löst.
Dein Beispielcode geht da schon in die richtige Richtung.
Das dürfte aber von der Umsetzung her nicht einfach werden und ggf. auch Fehleranfällig sein.
Den um das Thread Locking wirst du schon durch die Tasks nicht herum kommen.
Intern wirst du also auch mit einer ConcurrentCollection arbeiten müssen, sonst musst du nebem deinem expliziten Locking noch die Thread Locks auf die Collection regeln, was doppelter Aufwand wäre.

T-Virus
24.08.2019 21:55 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Palladin007 Palladin007 ist männlich
myCSharp.de-Mitglied

Dabei seit: 03.02.2012
Beiträge: 1.199
Entwicklungsumgebung: Visual Studio 2017
Herkunft: NRW

Themenstarter Thema begonnen von Palladin007

Palladin007 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Dass es eine solche Liste nicht gibt, hab ich mir schon gedacht ^^

Ich hatte gehofft, auf sowas wie eine Monitor-Klasse gehofft, die unabhängig vom Thread arbeitet, sondern nur mit einem Objekt als Key. Entweder das Objekt ist als "In Benutzung" markiert oder eben nicht, völlig egal, in welchem Thread der Code gerade läuft.

Damit könnte ich mir dann alles selber bauen:

C#-Code:
private object _lockKey = new object();

public void Add(T item)
{
    try
    {
        Monitor2.Lock(_lockKey);

        _innerList.Add(item);
    }
    finally
    {
        Monitor2.Release(_lockKey);
    }
}

public IDisposable TakeLock()
{
    Monitor2.Lock(_lockKey);

    return new DelegateDisposable(() => Monitor2.Release(_lockKey));
}

Das wäre nicht Mal sehr kompliziert und ließ sich beliebig auf jeden anderen Anwendungsbereich erweitern.

Zitat:
ein SemaphoreSlim verlangt nicht dass der freigebende Thread derselbe wie der sperrende Thread ist.

Stimmt - hatte ich nicht ganz auf dem Schirm.
Das setzt aber voraus, dass ich eine zweite List-Klasse bauen muss, die man anstelle des Originals benutzen muss, da ich in diesem Task nicht die alten Methoden (die jeweils mit Wait beginnen und Release enden) aufrufen darf.
Es wäre eine Lösung - zumindest theoretisch.

Zitat:
was ist mit ConfigureAwait?

Ich kann nicht zwingend davon ausgehen, dass es einen SynchronizationContext gibt, das wäre also sinnlos.
24.08.2019 22:52 Beiträge des Benutzers | zu Buddylist hinzufügen
Baumstruktur | Brettstruktur       | Top 
myCSharp.de | Forum Der Startbeitrag ist älter als ein Monat.
Der letzte Beitrag ist älter als ein Monat.
Antwort erstellen


© Copyright 2003-2019 myCSharp.de-Team | Impressum | Datenschutz | Alle Rechte vorbehalten. | Dieses Portal verwendet zum korrekten Betrieb Cookies. 14.10.2019 01:12