[C#] Simulatie met meerdere threads op Core2Duo niet sneller

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Knakker
  • Registratie: April 2000
  • Laatst online: 13-09 16:51
Ik werk aan een statistisch programmaatje dat een simulatie moet uitvoeren. Als ik die simulatie "gewoon" onder mijn Main-thread laat lopen, dan zie ik dat mijn CPU gebruik op 50% blijft hangen.

Ik wil deze simulatie dus uitvoeren op beide cores, dus ik dacht: dan maak ik twee Threads aan waarin ik de simulatie laat lopen.

code:
1
2
3
4
5
6
7
8
Thread[] Threads = new Thread[ProcessorCount];

for (int k = 0; k < ProcessorCount; k++)
{
    SimulationThreadParameters STP = new SimulationThreadParameters(... aantal parameters ...);
    Threads[k] = new Thread(new ParametrizedThreadStart(Simulate));
    Threads[k].Start(STP);
}


De code werkt goed en de simulatie geeft geen foutmeldingen. Met twee threads is de hele simulatie echter bijna 2x zo langzaam als wanneer ik de hele simulatie in mijn hoofdthread laat lopen. Hoe kan dit?

Alvast bedankt :)

Geef mij maar een Warsteiner.


Acties:
  • 0 Henk 'm!

  • Cloud
  • Registratie: November 2001
  • Laatst online: 17-09 10:39

Cloud

FP ProMod

Ex-moderatie mobster

Stomme vraag misschien, maar verdeel je het werk wel écht over die twee threads? :)
Nu start je gewoon twee threads met dezelfde entrypoint. Dus, voeren ze niet allebei je gehele Simulatie uit?

edit:
Ah shite, je hebt een parameterlist. Dan is dat waarschijnlijk niet van toepassing :)

[ Voor 20% gewijzigd door Cloud op 16-07-2008 16:36 ]

Never attribute to malice that which can be adequately explained by stupidity. - Robert J. Hanlon
60% of the time, it works all the time. - Brian Fantana


Acties:
  • 0 Henk 'm!

  • Knakker
  • Registratie: April 2000
  • Laatst online: 13-09 16:51
De SimulationThreadParameters class wordt per processor aangemaakt en doorgegeven aan de thread, dus dat is wel het geval.

Wel belangrijk detail: deze for-loop (2 processors) staat in een andere for-loop (5000 iteraties): vuur ik nu niet gewoon 10000 threads tegelijkertijd af? :P

Geef mij maar een Warsteiner.


Acties:
  • 0 Henk 'm!

  • HawVer
  • Registratie: Februari 2002
  • Laatst online: 13-09 16:51
Ligt er ook aan wat Knakker als parameters mee geeft. Kan het ook zijn dat beide threads vaak op elkaar moeten wachten waardoor de simulatie langzamer wordt?
Knakker schreef op woensdag 16 juli 2008 @ 16:44:
De SimulationThreadParameters class wordt per processor aangemaakt en doorgegeven aan de thread, dus dat is wel het geval.

Wel belangrijk detail: deze for-loop (2 processors) staat in een andere for-loop (5000 iteraties): vuur ik nu niet gewoon 10000 threads tegelijkertijd af? :P
Dat kun je eenvoudig controleren in taakbeheer. View->Select columns->Thread count

[ Voor 60% gewijzigd door HawVer op 16-07-2008 16:46 ]

http://hawvie.deviantart.com/


Acties:
  • 0 Henk 'm!

  • Cloud
  • Registratie: November 2001
  • Laatst online: 17-09 10:39

Cloud

FP ProMod

Ex-moderatie mobster

Knakker schreef op woensdag 16 juli 2008 @ 16:44:
Wel belangrijk detail: deze for-loop (2 processors) staat in een andere for-loop (5000 iteraties): vuur ik nu niet gewoon 10000 threads tegelijkertijd af? :P
Dat lijkt mij wel ja :) Dat kun je natuurlijk o.a. controleren door in je Task Manager, het aantal threads van je process te bekijken.
HawVer schreef op woensdag 16 juli 2008 @ 16:45:
Ligt er ook aan wat Knakker als parameters mee geeft. Kan het ook zijn dat beide threads vaak op elkaar moeten wachten waardoor de simulatie langzamer wordt?
Dat speelt natuurlijk ook mee. Wat voor simulatie hebben we het over?

Never attribute to malice that which can be adequately explained by stupidity. - Robert J. Hanlon
60% of the time, it works all the time. - Brian Fantana


Acties:
  • 0 Henk 'm!

  • Knakker
  • Registratie: April 2000
  • Laatst online: 13-09 16:51
Ter verduidelijking hierbij de structuur van de simulatie:

code:
1
2
3
4
5
6
7
For-loop met 5000 iteraties
   Voorbereiden van 2 matrices die als parameters gaan fungeren
   For-loop voor 2 processors
      Aan elke thread 1 matrix meegeven middels de class
      Thread starten
   Next
Next


Er is geen afhankelijkheid in de parameters tussen de iteraties; ik zou er bij wijze van spreken 10,000 tegelijkertijd uit kunnen voeren en hetzelfde resultaat krijgen.

Geef mij maar een Warsteiner.


Acties:
  • 0 Henk 'm!

  • Knakker
  • Registratie: April 2000
  • Laatst online: 13-09 16:51
Dat kun je eenvoudig controleren in taakbeheer. View->Select columns->Thread count
Geen touw aan vast te knopen, maar blijft tijdens de simulatie hangen rond de 30 terwijl het programma idle zo'n 20(?) heeft.

Geef mij maar een Warsteiner.


Acties:
  • 0 Henk 'm!

  • Cloud
  • Registratie: November 2001
  • Laatst online: 17-09 10:39

Cloud

FP ProMod

Ex-moderatie mobster

Knakker schreef op woensdag 16 juli 2008 @ 16:52:
Geen touw aan vast te knopen, maar blijft tijdens de simulatie hangen rond de 30 terwijl het programma idle zo'n 20(?) heeft.
Dat is natuurlijk niet gek, als elke Thread vrij snel klaar is met zijn Simulatie :) Maar daar zit waarschijnlijk het probleem dus wel, je start bergen met Threads tegelijkertijd op. Je zult het werk op andere wijze moeten verdelen, zodat je echt twee stuks hebt die je processor volledig kunnen benutten.

Never attribute to malice that which can be adequately explained by stupidity. - Robert J. Hanlon
60% of the time, it works all the time. - Brian Fantana


Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Had je al naar de Microsoft managed parallels extentions gekeken? Hier een MSDN Magazine artikel en een download link.

Op MSDN magazine is ook een artikel te vinden over PLINQ (Linq via parallels).

De parallel extentions zorgen voor multi-core support in C#/VB.net

En vergeet niet Program(.cs) te markeren met de MTAThead attribute. Standaard staat hier het STAThread attribute.

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Lijkt me zaak dat de TS eerst de basis van multithreading snapt voordat 'ie extra libaries e.d. in gaat schakelen.

@knakker, in dit soort situaties heb je normaliter een X aantal worker threads (over het algemeen gelijk aan of meer dan het aantal logische CPU's) welke uit een 'jobqueue' een taak lezen, deze uitvoeren, en de uitkomsten in een output queue/file lezen. Als het genereren van werk ook cpu-intensief is kun je dat door een aparte thread laten doen Dan ziet het er ongeveer zo uit

Parameters > Job Generation Thread > Queue > Job Execution Threads > Queue

Die Queues zijn de gesynchroniseerde punten, de threads accessen geen shared data o.i,d. zodat je niet op sycnhronisatiepunten zit te wachten,

[ Voor 72% gewijzigd door Hydra op 16-07-2008 17:46 ]

https://niels.nu


Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
Ik zou niet zomaar durven zeggen dat threadpooling hier verstanidg is. Vergeet niet dat hij maar tot zo'n 30 threads komt. Dat betekent dat elk work item erg klein is. Natuurlijk, het helpt nog steeds als je dan niet per work item een thread aanmaakt, maar dan nog is het distribueren van workitems over threads niet zomaar verstandig. Wat mij verstandgiger lijkt:
code:
1
2
3
4
5
For-loop voor 2 processors
   For-loop met 5000 iteraties
      1 matrix doorrekenen
   Next
Next

Dan start je maar 2 threads, en hoef je ook niet 10000 workitems over threads te verdelen.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
MSalters schreef op woensdag 16 juli 2008 @ 20:21:
Ik zou niet zomaar durven zeggen dat threadpooling hier verstanidg is. Vergeet niet dat hij maar tot zo'n 30 threads komt. Dat betekent dat elk work item erg klein is.
Pak je d'r 100 in plaats van 1 per keer. 2 threads hardcoden is ook alles behalve netjes.

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Knakker
  • Registratie: April 2000
  • Laatst online: 13-09 16:51
Ontzettend bedankt voor alle respons tot zover!

Ik heb even een nieuwe file aangemaakt met daarin een exterme versimpeling van mijn eigen simulatie. Ik heb de structuur aangepast zodanig dat er nu twee threads worden gestart die elk hun eigen iteraties afwerken. Het loopt al stukken beter, maar nog is het niet (of soms een heel klein beetje) sneller als dan dat ik de simulatie in de hoofdthread zou laten lopen.

Verder staat er nu nog een hele ranzige while loop in die zou moeten voorkomen dat het programma verder gaat als beide threads nog niet klaar zijn. Dat doet het niet goed (krijg heel af en toe foutmelding), dus: is er een elegantere manier?

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
private void Start(bool Threading)
{
    DateTime Start = DateTime.Now;
    List<double> Results = new List<double>();
    Thread[] T = new Thread[2];

    for (int p = 0; p < 2; p++)
    {
        if (Threading)
        {
            T[p] = new Thread(new ParameterizedThreadStart(SimulationThread));
            T[p].Start(Results);
        }
        else
        {
            SimulationThread(Results);
        }
    }
    if (Threading) { while ((T[0].ThreadState != ThreadState.Stopped) || (T[1].ThreadState != ThreadState.Stopped)) { } }

    double Sum = 0;
    for (int k = 0; k < Results.Count; k++) { Sum += Results[k]; }

    MessageBox.Show("Number of results: " + Results.Count + "\r\nAverage of simulation: " + (Sum / Results.Count) + "\r\nTotal time required: " + (DateTime.Now - Start).TotalMilliseconds + " ms.");
}

private void SimulationThread(object R)
{
    List<double> Results = new List<double>();
    for (int z = 0; z < 10000; z++)
    {
        OneSimulation(Results);
    }
    ((List<double>)R).AddRange(Results);
}

private void OneSimulation(object P)
{
    List<double> Results = (List<double>)P;
    Random R = new Random();
    for (int z = 0; z < 1000; z++)
    {
        Results.Add(R.NextDouble());
    }
}


Alvast bedankt! :)

Geef mij maar een Warsteiner.


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Thread.Join()?

https://niels.nu


Acties:
  • 0 Henk 'm!

  • NetForce1
  • Registratie: November 2001
  • Laatst online: 17-09 16:09

NetForce1

(inspiratie == 0) -> true

Heb je geen monitors in C#? In Java zou je daar bijv. een CountDownLatch voor kunnen gebruiken.

[ Voor 55% gewijzigd door NetForce1 op 17-07-2008 10:12 ]

De wereld ligt aan je voeten. Je moet alleen diep genoeg willen bukken...
"Wie geen fouten maakt maakt meestal niets!"


Acties:
  • 0 Henk 'm!

  • Cloud
  • Registratie: November 2001
  • Laatst online: 17-09 10:39

Cloud

FP ProMod

Ex-moderatie mobster

Knakker schreef op donderdag 17 juli 2008 @ 10:01:
C#:
1
2
3
// ...
    if (Threading) { while ((T[0].ThreadState != ThreadState.Stopped) || (T[1].ThreadState != ThreadState.Stopped)) { } }
// ...
Inderdaad, Thread.Join() gebruiken in plaats van de erg onelegante (understatement) regel code hierboven. En mocht je het echt zo willen doen, zet er dan minimaal een sleep in van een aantal milliseconden. Zo neemt je hoofdthread alsnog een berg cpu totdat je simulatie klaar is namelijk.

Dus
C#:
1
if (Threading) { while ((T[0].ThreadState != ThreadState.Stopped) || (T[1].ThreadState != ThreadState.Stopped)) { Thread.Sleep(100); } }

of bij voorkeur
C#:
1
2
T[0].Join();
T[1].Join();

edit:
oepsie, niet Thread.Join(); maar joinen op de daadwerkelijke threads :+

[ Voor 6% gewijzigd door Cloud op 17-07-2008 10:13 ]

Never attribute to malice that which can be adequately explained by stupidity. - Robert J. Hanlon
60% of the time, it works all the time. - Brian Fantana


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Cloud schreef op donderdag 17 juli 2008 @ 10:12:
Inderdaad, Thread.Join() gebruiken in plaats van de erg onelegante (understatement) regel code hierboven. En mocht je het echt zo willen doen, zet er dan minimaal een sleep in van een aantal milliseconden. Zo neemt je hoofdthread alsnog een berg cpu totdat je simulatie klaar is namelijk.
Inderdaad. Dat wordt een busy wait genoemd, en is een grote nono.
In software engineering, busy waiting or spinning is a technique in which a process repeatedly checks to see if a condition is true, such as waiting for keyboard input or waiting for a lock to become available. It can also be used to delay execution for some amount of time; this was necessary on old computers that had no method of waiting a specific length of time other than by repeating a useless loop a specific number of times, but on modern computers with clocks and different processor speeds, this form of time delay is often inaccurate and a sign of a naïve attempt at programming. Spinning can be a valid strategy in certain special circumstances, most notably in the implementation of spinlocks within operating systems designed to run on SMP systems. In general, however, it is considered an anti-pattern and should be avoided, as the CPU time spent waiting could have been reassigned to another task.
Wikipedia: Busy waiting

:P

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

NetForce1 schreef op donderdag 17 juli 2008 @ 10:12:
Heb je geen monitors in C#? In Java zou je daar bijv. een CountDownLatch voor kunnen gebruiken.
In C# zitten wel low-level lock mechanismes zoals monitors, mutexen en semaphores. Via de (nog in ontwikkeling zijnde, maar wel stabiele) parallels extentions zijn wel wat meer high-level technieken beschikbaar zoals CountDownEvent (vergelijkbaar met CountDownLatch). Ook bied het tevens een (generic) ConcurrentStack en ConcurrentQueue.

De CTP uit juni is zeer stabiel en het gebruik is zeer eenvoudig. Van Scott Hanselman had ik begrepen dat tussen nu en de volgende CTP (medio november) de parallels code ook op codeplex geplaatst zal worden op eenzelfde manier zoals asp.net mvc.

Threads kunnen bij incorrect gebruik ook de performance negatief beinvloeden. Met threads een CSV bestand verwerken zal de performance in de meeste gevallen niet goed doen. Echter een thread per CSV uit een bepaalde directory kan de boel weer wel versnellen. Veel taken zijn helaas niet echt geschikt voor concurrent processing.

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Niemand_Anders schreef op donderdag 17 juli 2008 @ 10:38:
Threads kunnen bij incorrect gebruik ook de performance negatief beinvloeden. Met threads een CSV bestand verwerken zal de performance in de meeste gevallen niet goed doen.
Compleet afhankelijk van de verhouding tussen lezen en rekenen. Als op elke regel intensieve berekeningen uitgevoerd moeten worden kan dat prima nuttig zijn. Ik snap dat het slechts een voorbeeld is, maar op deze manier lijkt het alsof file lezen in general niet multithreaded kan.

https://niels.nu


Acties:
  • 0 Henk 'm!

Verwijderd

Hydra schreef op donderdag 17 juli 2008 @ 10:44:
[...]maar op deze manier lijkt het alsof file lezen in general niet multithreaded kan.
Als we dan toch gaan muggeziften: 1 enkele file lezen doe je nooit multithreaded. Het verwerken van de informatie daarentegen zou je multithreaded kunnen doen.

Acties:
  • 0 Henk 'm!

  • Wilke
  • Registratie: December 2000
  • Nu online
Hydra schreef op donderdag 17 juli 2008 @ 10:44:
op deze manier lijkt het alsof file lezen in general niet multithreaded kan.
Natuurlijk kan dat wel, alleen het verdelen van een I/O-bound taak over meerdere threads is nogal zinloos ;) Met een peetje pech gaat het zelfs eens stuk langzamer dan inlezen in 1 thread. En sowieso kost het meer moeite.

Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Verwijderd schreef op donderdag 17 juli 2008 @ 11:53:
[...]
Als we dan toch gaan muggeziften: 1 enkele file lezen doe je nooit multithreaded.
Dat kan prima, je moet het leesstukje alleen synchroniseren.
Wilke schreef op donderdag 17 juli 2008 @ 12:15:
Natuurlijk kan dat wel, alleen het verdelen van een I/O-bound taak over meerdere threads is nogal zinloos ;) Met een peetje pech gaat het zelfs eens stuk langzamer dan inlezen in 1 thread. En sowieso kost het meer moeite.
Selectief quoten is ook een vak. Daarnaast klopt wat je zegt ook niet, in het geval van netwerkIO kan het zeker rendabel zijn om het werk te verdelen over verschillende threads omdat je bij veel kleine berichtjes juist vaak gebonden bent door netwerklatency.

[ Voor 52% gewijzigd door Hydra op 17-07-2008 12:49 ]

https://niels.nu


Acties:
  • 0 Henk 'm!

  • HawVer
  • Registratie: Februari 2002
  • Laatst online: 13-09 16:51
Knakker schreef op donderdag 17 juli 2008 @ 10:01:
C#:
1
2
3
[..]
    ((List<double>)R).AddRange(Results);
[..]


Alvast bedankt! :)
Een generic list is niet gegarandeerd thread safe. Nou ben ik niet geweldig goed op de hoogte van threads maar volgens mij moet je hier locking gebruiken als je vanuit twee threads dezelfde collectie wil aanspreken. Ik ben niet 100% zeker of dat ook nodig is voor de AddRange methode. :)

[ Voor 55% gewijzigd door HawVer op 17-07-2008 14:01 ]

http://hawvie.deviantart.com/


Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Iets met lock((ICollection)R).SyncRoot) inderdaad.

Maar volgens mij kan beter eerst de simulatie wat op de schop. Het lijkt wel alsof hier nu 5000x synchronisatie moet gaan plaatsvinden totdat 2 threads klaar zijn, daarna wordt pas weer doorgegaan in de hoofdthread die ook nog eens een berekening (som) moet doen. Dat lijkt me niet handig.
Waarschijnlijk kun je de simulatie gewoon splitsen in 5000/[aantal threads] taken per thread en die onafhankelijk van elkaar laten uitvoeren. Dat zal ietsje sneller gaan.

Je start nu 2 threads in het voorbeeld, maar dat kan eigenlijk eentje minder door de hoofdthread ook een taak op zich te laten nemen, maar dat is een beetje een minioptimalisatie.

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • Knakker
  • Registratie: April 2000
  • Laatst online: 13-09 16:51
Super! Ik heb zojuist de wijzigingen aangebracht in mijn eigen programma en het geheel draait nu stukken beter en sneller - de tijdswinst is ongeveer 45%. Het plaatsen van de Join in plaats van die ranzige while-loop maakte het geheel een stuk soepeler.

Even iets anders: ik wil een progress-bar toevoegen aan het geheel. Het probleem is dat de iteraties nu worden afgewerkt in separate threads. Kan ik het verloop van de threads (het aantal reeds afgewerkte iteraties) ook terugkoppelen naar mijn mainthread, zodat ik van daaruit de progressbar kan updaten?

[ Voor 3% gewijzigd door Knakker op 17-07-2008 16:51 ]

Geef mij maar een Warsteiner.


Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Ja, dat kan. :)

{signature}


Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Ook het gebruik van ICollection.SyncRoot is niet automatisch 'threadsafe' en kan bovendien voor race conditions zorgen. Ik zal als voorbeeld het Stack (uit System.Collections) nemen.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private static Stack s = new Stack();

public static void Main()
{
  for (int i = 0; i < 10; i++)
     s.Push(i);

  for(int i = 0; i < 3; i++)
  {
     ThreadStart ts = new ThreadStart(ConsumeStack);
     Thread t = new Thread(ts);
     t.Start();
  }
}

public void ConsumeStack()
{
  if (s.SyncRoot.Count > 0)
  {
     object item = s.SyncRoot.Pop();
     //do iets met 'item'
  }
}

Het probleem is dat Count, Peek en Pop niet gelijktijdig worden uitgevoerd. Tussen de tijd dat de Count (Regel 18) en Pop (regel 20) wordt aangeroepen kan op een andere thread net dat laatste item al zijn opgehaald. Pop uitvoeren op een stack welke leeg is, levert een InvalidOperationException op. Meerdere threads kunnen immers een count = 1 terug krijgen. Een en ander kun je wel weer oplossen met een mutex. Echter door het gebruik van SyncRoot en Mutex zal je performance in 9 van de 10 vervallen slechter zijn, dan als je het gewoon als single thread had uitgevoerd. Een lock plaatsen om om het if statement zal ervoor zorgen dat de drie thread sequentieel worden uitgevoerd.

Effectiever is dan het gebruik van een Semaphore instantie in de main thread, waarbij de main thread de pop value meegeeft aan ParameterizedThreadStart (waardoor het niet meer nodig is om de stack te synchroniseren). De overhead van het restarten van een (nieuwe) thread is lager dan een SyncRoot call. In het .NET framework zijn voornamelijk method call thread-safe, niet de objecten zelf. Het C# lock statement (SyncLock in VB) is slechts een alias naar Monitor.

Daarom heb ik ook al enkele malen de focus op de parallels extentions proberen te leggen. Daarin worden juist dit soort issues opgepakt waardoor je niet meer met infrastructuur hoeft bezig te houden.

Nu zal iedereen die zich enigszins regelmatig bezig houd met threading zijn eigen thread safe collections verzameling hebben opgebouwd. De System.Threading.Collections namespace van het parallels project is juist opgezet om dit gat te vullen.

Een zeer goed artikel over het identificeren van concurrent issue is te vinden in juni 2008 uitgave van MSDN magazine (US versie).

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Niemand_Anders schreef op donderdag 17 juli 2008 @ 17:01:
Een lock plaatsen om om het if statement zal ervoor zorgen dat de drie thread sequentieel worden uitgevoerd.
Tuurlijk kan dat ook anders:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    private static Stack<int> s = new Stack<int>();
    ...
        int item;
        while (true) 
        {
            lock (((ICollection)s).SyncRoot)
            {
                if (s.Count>0)
                    item = s.Pop();
                else
                    break;
            }
            //do iets met 'item' 
        }


@TS: het keyword is bijvoorbeeld 'BackGroundWorker'

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 23:16
Niemand_Anders schreef op donderdag 17 juli 2008 @ 17:01:

Daarom heb ik ook al enkele malen de focus op de parallels extentions proberen te leggen. Daarin worden juist dit soort issues opgepakt waardoor je niet meer met infrastructuur hoeft bezig te houden.
Op welke manier dan ?
Lees ik hier nu goed dat je zegt dat je met Parallel Extensions je niet meer hoeft bezig te houden met locking en thread sync, want dan denk ik dat je het toch bij het verkeerde eind hebt.

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • HawVer
  • Registratie: Februari 2002
  • Laatst online: 13-09 16:51
Knakker schreef op donderdag 17 juli 2008 @ 16:51:
Super! Ik heb zojuist de wijzigingen aangebracht in mijn eigen programma en het geheel draait nu stukken beter en sneller - de tijdswinst is ongeveer 45%. Het plaatsen van de Join in plaats van die ranzige while-loop maakte het geheel een stuk soepeler.

Even iets anders: ik wil een progress-bar toevoegen aan het geheel. Het probleem is dat de iteraties nu worden afgewerkt in separate threads. Kan ik het verloop van de threads (het aantal reeds afgewerkte iteraties) ook terugkoppelen naar mijn mainthread, zodat ik van daaruit de progressbar kan updaten?
Een backgroundworker is een eenvoudig maar is niet perse nodig. De background worker is bedoeld om asynchroon een taak uit te voeren zonder invloed uit te oefenen op de main thread. Het splitsen van veel taken over veel threads kun je beter niet met een backgroundworker doen. Meerdere threads uitvoeren wordt met behulp van backgroundworkers zelfs lastiger.

Je kan vanuit de thread een functie aanroepen die zich normaal zou uitvoeren op de mainthread. Je kan dan Control.BeginInvoke gebruiken om de aanroep te delegeren van je eigen thread naar de main/ui thread. Met google moet je verder een heel eind kunnen komen.

http://hawvie.deviantart.com/


Acties:
  • 0 Henk 'm!

  • HawVer
  • Registratie: Februari 2002
  • Laatst online: 13-09 16:51
Bedankt voor de info. Zo leer ik ook weer wat bij over threads. Overigens lijkt de dit voor de TS wat overkill. Eventueel kan het resultaat geplitst worden over twee collecties die na het joinen van de thread gemerged worden. De vraag is alleen of de threads en of de resultaten afhankelijk van elkaar zijn.

[ Voor 73% gewijzigd door HawVer op 17-07-2008 22:51 ]

http://hawvie.deviantart.com/


Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

whoami schreef op donderdag 17 juli 2008 @ 22:02:
[...]
Op welke manier dan ?
Lees ik hier nu goed dat je zegt dat je met Parallel Extensions je niet meer hoeft bezig te houden met locking en thread sync, want dan denk ik dat je het toch bij het verkeerde eind hebt.
Nee, juist de parallels collecties doen dat voor jouw. Thread syncing moet altijd voorkomen worden in multi threaded applicaties. Zolang je synchronisatie mechanismes nodig hebt, wordt de performance van je applicatie tegen gehouden. Vooral 'threaded' collections hebben een negatieve impact op de performance van een applicatie.

Zodra een applicatie monitor gebruikt, verplaatst de CLR alle threads uit dat applicatie domein naar dezelfde core waardoor je applicatie feitelijk Single Threaded Apartment (STA) wordt en jouw applicatie nog slechts een enkele core gebruikt. Standaard draaien .net applicaties in STA mode. Dit kan eenvoudig veranderd worden door het MTAThread attribute te zetten in Program.cs. Op dat moment zal de CLR threads ook verdelen over meerdere cores/cpu's. Om die reden levert een Form.ShowModal call ook een exceptie op. ShowModal wordt alleen ondersteund bij appliacties welke in STA mode draaien.

Vandaar dat ik ook aangaf dat je dat kunt voorkomen door de main thread de collectie te laten beheren en het item uit de collectie meegeven als parameter van de thread. Op die manier voorkom je threading dependencies en dat je applicatie terug schakelt van MTA mode naar STA. Dit terug schakelen is (hoe raar het ook klinkt) een performance optimalisatie. Synchroniseren over meerdere cores is lastiger (meer overhead) dan op een enkele core.

Met parallels probeert Microsoft developers welke zich bezig houden met multi threaded applicaties een geoptimaliseerde library te bieden welke een aantal zeer regelmatig infrastructuur oppakt. Daarbij wordt aangeraden om alle gegevens welke de thread nodig heeft te verzamelen in een structure en deze als parameter aan de thread mee te geven. Parallels maakt enkele threading gerelateerde zaken een stuk eenvoudiger.


Ik zag trouwens dat ik in mijn voorbeeld onze gemodificeerde stack had gebruikt. Daar heeft SyncLock zichzelf al gesynchroniseerd terug. De standaard Stack geeft slechts een object reference terug welke gebruikt kan worden voor synchronisatie. Excuus voor enige verwarring welke misschien is ontstaan bij sommigen.

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • Knakker
  • Registratie: April 2000
  • Laatst online: 13-09 16:51
Overigens lijkt de dit voor de TS wat overkill.
Ik moet zeggen dat ik niets wist dat je zoveel kon leren in een dusdanig korte tijd _O_.

De simulatie draait als een speer; de progress bar loopt nog niet heel soepel, maar ik ben reeds bezig met datgene dat jij aan stipte (invoke richting UI thread). Ik heb ook geprobeerd de simulatie over twee backgroundworker threads te laten lopen, maar daar was ik performance-wise niet happy mee. Bovendien moet de simulatie helemaal niet op de achtergrond lopen, maar op de voorgrond - bovendien duurt deze nu 0.5sec, dus daar kun je wel op wachten.

Ik maak overigens u gebruik van het lock statement om aan het einde van elke iteratie de resultaten naar een gedeelde verzameling te schrijven. Ik heb echt al duizend keer de simulatie gestart en ben nog geen fouten tegengekomen; begrijp ik uit de reacties hierboven dat dit theoretisch wel mogelijk is?

Geef mij maar een Warsteiner.


Acties:
  • 0 Henk 'm!

  • HawVer
  • Registratie: Februari 2002
  • Laatst online: 13-09 16:51
Knakker schreef op vrijdag 18 juli 2008 @ 14:17:
[...]
Ik maak overigens u gebruik van het lock statement om aan het einde van elke iteratie de resultaten naar een gedeelde verzameling te schrijven. Ik heb echt al duizend keer de simulatie gestart en ben nog geen fouten tegengekomen; begrijp ik uit de reacties hierboven dat dit theoretisch wel mogelijk is?
De locking die hier zelf uitvoert is een vorm van synchronisatie. Je code is nu dus thread safe.

Maar dat is niet wat niemand_anders aangeeft. Hij geeft aan dat er andere nieuwere technieken zijn die out of the box thread safe zijn. Wat hij aangeeft is dat je eigen synchronisatie per definitie een negatieve invloed heeft op de performance. Ook geeft hij aan hoe je voordeel kan halen uit meerdere core's dmv de paralel extensions.

http://hawvie.deviantart.com/


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 23:16
Die technieken (Parallel Extensions) die Niemand_Anders aangeeft, zijn nog niet 'echt' gereleased; het zijn nog maar CTP's.
Daarbij bevat die extensions op dit moment slechts 2 thread safe collections nl een stack en een queue.
Hoedanook, 't is wel een veelbelovend hulpmiddel.

Niemand_Anders
Thread syncing moet altijd voorkomen worden in multi threaded applicaties.
Euh .... Tja, soms kan je niet anders. Thread A moet soms wachten op Thread B en Thread C. Locking heb je ook in sommige gevallen nodig, hoe ga je anders races aanpakken ?

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 08-09 11:33
whoami schreef op zondag 20 juli 2008 @ 23:11:
Die technieken (Parallel Extensions) die Niemand_Anders aangeeft, zijn nog niet 'echt' gereleased; het zijn nog maar CTP's.
Daarbij bevat die extensions op dit moment slechts 2 thread safe collections nl een stack en een queue.
Hoedanook, 't is wel een veelbelovend hulpmiddel.

Niemand_Anders
Thread syncing moet altijd voorkomen worden in multi threaded applicaties.
Euh .... Tja, soms kan je niet anders. Thread A moet soms wachten op Thread B en Thread C. Locking heb je ook in sommige gevallen nodig, hoe ga je anders races aanpakken ?
Ik heb het artikel dat niemand_anders poste gelezen (super interessant) het voordeel van parallels zijn niet die 2 threadsafe collections maar dat je zoveel kleine taken multi-threaded kan uitvoeren. Simpele for-loops kunnen ineens multi-threaded en ook futures wat echt een brilliant (doch oud) concept is is ontzettend handig. Ook hebben de MS Devs alle functies erg goed uitgedacht waardoor zelfs als de functies sequentieel worden uitgevoerd ze eigenlijk nooit langzamer zijn dan normaal uitvoeren. (Zoals futures als ze nog niet zijn berekend gewoon uitgevoerd worden door het calling-thread waardoor het eigenlijk gewoon een method call wordt).

Ik denk dat als je zodadelijk een applicatie maakt die vanaf het begin af aan alle mogelijkheden van parallel FX gebruikt het echt een stuk sneller gaat, en "The Free Lunch" ( http://www.gotw.ca/publications/concurrency-ddj.htm ) is weer terug dankzij dit framework omdat het automatisch gebruik maakt van eventuele extra processoren. Zoals vroeger software automatisch gebruik maakte van snellere processoren. (Een programma ontwikkeld voor de pentium 3 was op een pentium 4 automatisch sneller zonder dat je wat hoefde te doen).

~ Mijn prog blog!


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
whoami schreef op zondag 20 juli 2008 @ 23:11:
Niemand_Anders
Thread syncing moet altijd voorkomen worden in multi threaded applicaties.
Euh .... Tja, soms kan je niet anders. Thread A moet soms wachten op Thread B en Thread C. Locking heb je ook in sommige gevallen nodig, hoe ga je anders races aanpakken ?
Ik neem aan dat hij bedoelt dat het zoveel mogelijk voorkomen moet worden. Lijkt me duidelijk.

https://niels.nu


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 23:16
roy-t schreef op maandag 21 juli 2008 @ 10:10:
[...]


Ik heb het artikel dat niemand_anders poste gelezen (super interessant) het voordeel van parallels zijn niet die 2 threadsafe collections maar dat je zoveel kleine taken multi-threaded kan uitvoeren. Simpele for-loops kunnen ineens multi-threaded en ook futures wat echt een brilliant (doch oud) concept is is ontzettend handig. Ook hebben de MS Devs alle functies erg goed uitgedacht waardoor zelfs als de functies sequentieel worden uitgevoerd ze eigenlijk nooit langzamer zijn dan normaal uitvoeren. (Zoals futures als ze nog niet zijn berekend gewoon uitgevoerd worden door het calling-thread waardoor het eigenlijk gewoon een method call wordt).
I know .... Ik heb de Parallels Extension hier staan ...
roy-t schreef op maandag 21 juli 2008 @ 10:10:
[...]


Ik denk dat als je zodadelijk een applicatie maakt die vanaf het begin af aan alle mogelijkheden van parallel FX gebruikt het echt een stuk sneller gaat, en "The Free Lunch" ( http://www.gotw.ca/publications/concurrency-ddj.htm ) is weer terug dankzij dit framework omdat het automatisch gebruik maakt van eventuele extra processoren. Zoals vroeger software automatisch gebruik maakte van snellere processoren. (Een programma ontwikkeld voor de pentium 3 was op een pentium 4 automatisch sneller zonder dat je wat hoefde te doen).
Niet mee eens.... Het is niet omdat je dit framework nu gebruikt, dat alle problemen daarmee opgelost zijn (of beter, dat je daarom in alle gevallen optimaal gebruik maakt van alle cores).

[ Voor 34% gewijzigd door whoami op 21-07-2008 11:38 ]

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Vrijwel alle multithreaded applicaties welke ik schrijf c.q. onderhoud zijn gebaseerd op distributed application designs. Elke thread kan gezien worden als een 'standalone' applicatie. De hoofd thread fungeert als dispatcher om de threads te starten.

Locking kun je ook deels voorkomen door een event handler te gebruiken. Via ISynchronizeInvoke kun je dan het event naar de hoofdtread sturen en de hoofdthread is de enigste welke een locking op de collectie uitvoert om het resultaat op te slaan. Er zijn verschillende methoden om synchronisatie te voorkomen.

Locking is opzich geen probleem zolang slechts 1 thread de locking uitvoert. Multi threaded locking hebben namelijk kernel hooks nodig en gaan daardoor buiten de CLR om. Locking op de hoofd thread (het process welke is opgestart door de CLR) wordt door de CLR uitgevoerd zolang er geen multi threaded lock nodig is.

Door alleen locking op de hoofd thread te gebruiken (in de dispatcher en event handler) is het vrij eenvoudig om multi threaded synchronisatie te voorkomen. Zelf ontwikkel ik geen threads welke weer afhankelijk zijn van andere threads.

Wat betreft de CTP versies. Geen van de vorige versies heeft een verandering gebracht in bestaande API. Elke CTP bevat eigenlijk vooral meer speelgoed voor multi threaded applicaties.

Stel je wilt een matrix van 10x10 printen. Je zou via een loop 10 maal een thread kunnen starten welke elk een rij afdrukt. Maar waarschijnlijk is de winst dan vrij minimaal. De meeste threaded zullen dan de CPU power moeten delen en de overhead zal een groot deel van de performance winst teniet doen. Via een Semaphore in de loop slecht 2 of 3 theads starten zal in de meeste gevallen meer winst opleveren (de CPU schedular is dan minder kwijt de resources te verdelen). Voor de meeste applicaties zijn threads voornamelijk interessant als er veel idle tijd is (bijv. wachten op database of netwerk response). Rekenkundige threads hebben over het algemeen weinig idle (lees: alle threads willen processor tijd) tijd en zal de winst minimaal zijn. Rekenkundige threads halen de meeste winst als zij worden verdeeld over meerdere cores. Echter verplaatst de CLR deze threads allemaal naar dezelfde thread zodra er threads gesynchroniseerd dienen te worden.

Al onze multi threaded applicaties hebben een ingebouwde 'profiler' test procedure. Deze profiler test voert de verschillende kerntaken 100 maal uit. De profiler gebruikt na elke run een thread meer totdat de uitvoer langer duurt dan de vorige. Elke run wordt driemaal uitgevoerd en het gemiddelde uitvoeringstijd wordt gebruikt voor de vergelijking. De tests slaan uiteindelijk de optimale waardes op in de app.config en vormen daarmee de maximum waarde van de semaphores.

Soms zijn synchronisaties echter niet te voorkomen. Anders dan in C/C++ regelt de CLR heel veel zaken voor jouw waar je geen of weinig invloed op hebt. Een van die zaken is de fallback van multi threaded apartment naar single threaded apartment (applicaties draaien dan op een enkele core). De processor instructie pipelines worden ook 'threads' genoemd.

Distributed application design patterns vormen vaak een goede basis voor een multi threaded applicatie. Een aantal goede design patterns hiervoor zijn te vinden op http://www.cs.wustl.edu/~schmidt/patterns-ace.html (C++ based, maar theorie is ook bruikbaar voor c#/vb developers) en op http://www.cmcrossroads.com/bradapp/javapats.html zijn distributed design patterns (en de Gang of Four patterns) voor java.

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • HawVer
  • Registratie: Februari 2002
  • Laatst online: 13-09 16:51
Niemand_Anders schreef op maandag 21 juli 2008 @ 12:01:
Locking is opzich geen probleem zolang slechts 1 thread de locking uitvoert. Multi threaded locking hebben namelijk kernel hooks nodig en gaan daardoor buiten de CLR om. Locking op de hoofd thread (het process welke is opgestart door de CLR) wordt door de CLR uitgevoerd zolang er geen multi threaded lock nodig is.
Wat zijn de nadelen van het gebruik van een kernel hook? Kost dat zoveel extra tijd?

http://hawvie.deviantart.com/


Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

De CLR van .net is van nature vrij complex (als in veel mogelijkheden) en heeft ingebouwde managed locking mechanismes (zoals monitor). De System.Threading.WaitHandle (waarvan Semaphore, Mutex erven) is een system wide locking/synchronisatie mechanisme. Deze locks zijn krachtig maar omdat ze buiten de CLR om werken resource intensiever. Voor het OS is het gemakkelijker om threads op dezelfde core te syncen, dan als deze over meedere cores verspeid zijn. De CLR grijpt daarom ook in door de 'systeem' threads naar dezelfde core te verplaatsen zodat vervolg synchronisaties minder resources (en daarmee processor tijd) kost. Runtime thread locks (o.a. monitor) worden alleen door de CLR afgehandeld en wordt ook weleens aangeduid als een managed lock.

In het verleden heb ik weleens de Win32 api (CreateMutex en ReleaseMutex) erbij gepakt en zelf de locking opgepakt. Daarmee kun je ook voorkomen dat een MTA app door de CLR wordt omgezet naar een STA app. De main thread van via Thread.TrySetApartment de state nog wel veranderen naar MTA voordat de subthread start, maar regelmatig switchen tussen STA en MTA kost aardig wat cpu time.

Bij een enkele cross thread lock kun je daarmee ervoor zorgen dat je applicatie in MTA mode blijft draaien. Via IntPtr creert de CLR dan een object (integer) met tevens een reference op de kernel stack. Deze handle kun je vervolgens gebruiken met de win32 mutex calls. Uiteraard is windows zelfs nog wel tijd kwijt met de synchronisatie lock. Veelvuldig op kernel niveau synchroniseren heeft dus een zeer negatieve impact op de performance.

Van Jeffrey Richter heb ik ooit begrepen dat managed locks werken door het reference object van de caller aan een resource te koppelen op de CLR stack. Als de CLR ziet dat een andere caller probeert dan de resource te benaderen geeft deze een exceptie. Op het moment dat je een kernel lock gebruikt wordt er behalve een integer (IntPtr) (structures verblijven op de stack, terwijl objecten (classes) op de heap verblijven en enkel een reference daarna toe op de stack hebben) op de stack, ook een unmanaged integer aangemaakt (pointer) buiten de CLR. Echter de instantie van het locking mechanisme (instance van bijv. Mutex) staat op de heap. De CLR moet dan samen met de kernel de toegang tot de managed en unmanaged instances synchroniseren en dat kost dus tijd. Er wordt op dat moment dus eigenlijk een dubbele synchronisatie uitgevoerd. Dat is helaas het nadeel van een runtime omgeving.

Als je dus door een onderdeel te herschrijven een synchronisatie punt kunt weg werken, kun je dus al relatief wat winst bereiken. De hoeveelheid winst is vooral afhankelijk van de idle times welke in een instructie pipelines ontstaan. Als jouw blokje code geen afhankelijkheden heeft is de performance winst afhankelijk van het aantal execution engines welke een core tot zijn beschikking heeft.

Vrijwel al mijn multi threaded applicaties gebruiken een private message queue (welke wordt gelezen door de main thread) om informatie uit een subthread weer bij de hoofd thread te bezorgen. De message queue receive handler vuurt op basis van de berichten dan de juiste event af (ThreadFinished, ThreadException, ThreadCancelled, etc).

In principe zorgt het gebruik van meerdere threads voor een performance winst en elke synchronisatie call zorgt voor performance verlies. De optelsom van beide bepaald uiteindelijk of er winst overblijft.

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 23:16
Niemand_Anders schreef op maandag 21 juli 2008 @ 17:06:
Vrijwel al mijn multi threaded applicaties gebruiken een private message queue (welke wordt gelezen door de main thread) om informatie uit een subthread weer bij de hoofd thread te bezorgen. De message queue receive handler vuurt op basis van de berichten dan de juiste event af (ThreadFinished, ThreadException, ThreadCancelled, etc).

In principe zorgt het gebruik van meerdere threads voor een performance winst en elke synchronisatie call zorgt voor performance verlies. De optelsom van beide bepaald uiteindelijk of er winst overblijft.
Talk is cheap, show me the code. :P
plz

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
@Niemand_Anders: +3: inzichtvol

Thanks :)

https://niels.nu


Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Of ik heb het verhaal van Niemand_Anders verkeerd gelezen, of er staat niet duidelijk bij dat STA vs MTA een legacy-iets is. Het is alleen relevant bij COM-interactie is (denk aan clipboard/ActiveX controls). Wat het met ShowDialog('ShowModal') te maken heeft zie ik al helemaal niet.
Of, om de documentatie te quoten:
The .NET Framework does not use apartments, and managed objects are responsible for using all shared resources in a thread-safe manner themselves.
Ik vraag me ook af hoe de 'private message queue' precies werkt. Is dit een queue die toevallig intern met locks werkt? :)

Dat neemt niet weg dat dingen als PLINQ de toekomst zijn.

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 07:07

Haan

dotnetter

Ik moet zeggen dat ik dit een mateloos interessant topic vind!
Is het ook misschien niet een leuk idee om hiervan een meer algemeen topic te maken over multi-threading?
Iets als 'Het grote multi-threading topic - Deel 1' o.i.d.?

Nog een vraagje: is een koppeling tussen twee databases (die 's nachts draait)
[=is een programma dat een aantal CSV bestanden inleest (met daarin een database dump van enkele tienduizenden records), en deze vervolgens vergelijkt met een andere database en daar bestaande records in update/creëert]
relatief makkelijk multithreaded te maken?

Kater? Eerst water, de rest komt later


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 23:16
Haan schreef op dinsdag 22 juli 2008 @ 09:59:

Is het ook misschien niet een leuk idee om hiervan een meer algemeen topic te maken over multi-threading?
Iets als 'Het grote multi-threading topic - Deel 1' o.i.d.?
Nee, ik vind van niet.
'Grote' topics verzanden dan meestal in allemaal korte vraagjes, en zijn moeilijk beheersbaar. Daarbij denk ik ook dat je een 'groot multithreading' topic gauw minder interessant zal worden omdat je op den duur door de bomen het bos niet meer zal zien.
Nog een vraagje: is een koppeling tussen twee databases (die 's nachts draait)
[=is een programma dat een aantal CSV bestanden inleest (met daarin een database dump van enkele tienduizenden records), en deze vervolgens vergelijkt met een andere database en daar bestaande records in update/creëert]
relatief makkelijk multithreaded te maken?
Ik denk het wel. Per CSV bestand kan je een 'task' starten die dat CSV bestand verwerkt en vergelijkt met een andere database. Je hebt dan ook al het geluk dat de database de nodige locking mechanismen al voor jou kan doen.
Maar, wat is de bedoeling hiervan ? Is dat een soort home-brew replicatie ?

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • HawVer
  • Registratie: Februari 2002
  • Laatst online: 13-09 16:51
Haan schreef op dinsdag 22 juli 2008 @ 09:59:
Nog een vraagje: is een koppeling tussen twee databases (die 's nachts draait)
[=is een programma dat een aantal CSV bestanden inleest (met daarin een database dump van enkele tienduizenden records), en deze vervolgens vergelijkt met een andere database en daar bestaande records in update/creëert] relatief makkelijk multithreaded te maken?
Dat hangt van een aantal zaken af. Het openen van het bestand en het uitlezen van de regels kun je multithreaded doen maar levert weinig tot niets op. Het inlezen van een batch regels, bijvoorbeeld 50 zal elke thread na elkaar moeten doen. Dus of één thread dit doet of meerdere levert in dit geval niet veel performance winst op. Het wordt pas interessant tijdens het verwerken van de regels. Je kan bijvoorbeeld een batch regels gaan verwerken terwijl een andere thread de volgende vijftig regels gaat inlezen. Je kunt er ook voor kiezen om meerdere batches tegelijk te verwerken in meerdere threads. Het is dan wel belangrijk dat je erg zorgvuldig omgaat met concurrency. Stel dat batch 1 een vergelijking uitvoert op regel 1 en daarna batch 2 een wijziging uitvoert op regel 1 dan is het resultaat van de vergelijking in batch 1 niet meer betrouwbaar. Dus relatief makkelijk als je voldoende voorkennis hebt en zorgvuldig test.

http://hawvie.deviantart.com/


Acties:
  • 0 Henk 'm!

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 07:07

Haan

dotnetter

whoami schreef op dinsdag 22 juli 2008 @ 11:02:
Ik denk het wel. Per CSV bestand kan je een 'task' starten die dat CSV bestand verwerkt en vergelijkt met een andere database. Je hebt dan ook al het geluk dat de database de nodige locking mechanismen al voor jou kan doen.
Maar, wat is de bedoeling hiervan ? Is dat een soort home-brew replicatie ?
Het is een koppeling tussen een legacy applicatie waar men geen afstand van kan nemen en een nieuw systeem.
Maar ik ga eerst wel wat meer oefenen op eigen progjes, dat is wat veiliger ;)

Kater? Eerst water, de rest komt later


Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Niemand_Anders schreef op maandag 21 juli 2008 @ 17:06:
Zodra een applicatie monitor gebruikt, verplaatst de CLR alle threads uit dat applicatie domein naar dezelfde core waardoor je applicatie feitelijk Single Threaded Apartment (STA) wordt en jouw applicatie nog slechts een enkele core gebruikt. Standaard draaien .net applicaties in STA mode. Dit kan eenvoudig veranderd worden door het MTAThread attribute te zetten in Program.cs. Op dat moment zal de CLR threads ook verdelen over meerdere cores/cpu's. Om die reden levert een Form.ShowModal call ook een exceptie op. ShowModal wordt alleen ondersteund bij appliacties welke in STA mode draaien.
-------------------------------------------------
De CLR van .net is van nature vrij complex (als in veel mogelijkheden) en heeft ingebouwde managed locking mechanismes (zoals monitor). De System.Threading.WaitHandle (waarvan Semaphore, Mutex erven) is een system wide locking/synchronisatie mechanisme. Deze locks zijn krachtig maar omdat ze buiten de CLR om werken resource intensiever. Voor het OS is het gemakkelijker om threads op dezelfde core te syncen, dan als deze over meedere cores verspeid zijn. De CLR grijpt daarom ook in door de 'systeem' threads naar dezelfde core te verplaatsen zodat vervolg synchronisaties minder resources (en daarmee processor tijd) kost. Runtime thread locks (o.a. monitor) worden alleen door de CLR afgehandeld en wordt ook weleens aangeduid als een managed lock.
-------------------------------------------------
Locking is opzich geen probleem zolang slechts 1 thread de locking uitvoert. Multi threaded locking hebben namelijk kernel hooks nodig en gaan daardoor buiten de CLR om. Locking op de hoofd thread (het process welke is opgestart door de CLR) wordt door de CLR uitgevoerd zolang er geen multi threaded lock nodig is.

Door alleen locking op de hoofd thread te gebruiken (in de dispatcher en event handler) is het vrij eenvoudig om multi threaded synchronisatie te voorkomen. Zelf ontwikkel ik geen threads welke weer afhankelijk zijn van andere threads.
-------------------------------------------------
Soms zijn synchronisaties echter niet te voorkomen. Anders dan in C/C++ regelt de CLR heel veel zaken voor jouw waar je geen of weinig invloed op hebt. Een van die zaken is de fallback van multi threaded apartment naar single threaded apartment (applicaties draaien dan op een enkele core). De processor instructie pipelines worden ook 'threads' genoemd.
Daar wil ik wel eens een bron van zien.

Mocht zo'n gedrag in de CLR zitten dan denk ik dat het hier wel in zou staan.
Bovendien ben ik nu al even aan het googlen op iets wat er ook maar op lijkt en kan ik helemaal niets vinden.

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 23:16
Daar ben ik eigenlijk stiekem ook wel benieuwd naar :P

https://fgheysels.github.io/


  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Sorry, voor de late reply, maar ik zit momenteel op mijn vakantie adres (Zadar, Kroatie). Veel informatie aangereikt door Jeffrey Richter wordt niet door Microsoft onderkent noch ontkent. Wat wel feitelijk vast staat is dat in .NET 2.0 de CLR engine een nieuw thread model heeft gekregen. Zo start een applicatie nu standaard in MTA mode (winform apps hebben nog steeds in program.cs de STAThread attribute), terwijl dit in 1.x nog standaard STA mode was. Ten tijde van 2.0 kon de CLR ook geen onderscheid maken tussen een dubbele processor en een multicore CPU. De multicore CPU heeft namelijk een gemeenschappelijk basis met daarop 2 of 4 uitvoerings eenheden. Alle cores kunnen bij alle instructie pipelines. Bij een dubbele processor is dat niet mogelijk en is synchronisatie complexer.

Omdat de CLR wel al support voor meerdere processors is in het 2.0 threading model in elk geval opgenomen dat subthreads altijd op dezelfde processor leven als de hoofd thread. Van wat ik heb begrepen is Microsoft's standpunt in deze dat zij nooit de interne werking kunnen garanderen (c.q. behouden) en daarom daarover ook geen uitspraken doen.

Ik weet dat Microsoft wel heeft geexperimenteerd met multicore support in de CLR. Het is niet bekend of deze ooit in 3.0 of 3.5 is toegevoegd.

Als de MTA mode zoals de MSDN documentatie aangeeft alleen van invloed is op (D)COM(+) componenten, dan is het vreemd dat zij dit in 2005 ineens veranderen, terwijl het aantal programmeurs van (D)COM(+) componenten sinds het .NET framework juist flink is afgenomen.

Zodra ik terug ben van vakantie (volgende week donderdag) zal ik alle mails welke ik heb ontvangen van Jeffrey en enkele Microsoft programmeurs verzamelen. Ik zal ook eens kijken hoever de chat history van mijn IRC client terug gaat.

Overgens de AMD PDF raad nog steeds aan (sectie Lock Optimalisations) om locks (synchronisatie) te voorkomen zoals 'enter late, leave early' en 'lock only when necessary' en data te kopieren voor voordat de thread wordt gestart zodat subtreads geen (of minimaal) resources van de hoofd hoeven te raadplegen.

Overigens gaat ook de PDF over een multi core CPU. Net zoals de Intel processors delen alle processing units dezelfde basis en heeft de pipeline verplaatsing vooral een negatieve impact. Probleem schijnt de hardware abstractie van windows te zijn. Windows zal een AMD QuadCore processor zien als 4 processors en daardoor ziet de CLR ook 4 processors. Echter zijn deze virtueel omdat een OS geen taak aan een specifieke core kan hangen. Er is dus 1 queue met 4 consumers. Bij een dual quadcore systeem zijn er 2 queues met elk 4 consumers. En wat de wat STA mode in 2.0 doet is ervoor zorgen dat zoveel mogelijk threads op 1 processor komen zodat er geen processor synchronisatie nodig is.

Ik kom hier nog op terug.

If it isn't broken, fix it until it is..


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Niemand_Anders schreef op donderdag 21 augustus 2008 @ 15:37:
Sorry, voor de late reply, maar ik zit momenteel op mijn vakantie adres (Zadar, Kroatie). Veel informatie aangereikt door Jeffrey Richter wordt niet door Microsoft onderkent noch ontkent. Wat wel feitelijk vast staat is dat in .NET 2.0 de CLR engine een nieuw thread model heeft gekregen. Zo start een applicatie nu standaard in MTA mode (winform apps hebben nog steeds in program.cs de STAThread attribute), terwijl dit in 1.x nog standaard STA mode was. Ten tijde van 2.0 kon de CLR ook geen onderscheid maken tussen een dubbele processor en een multicore CPU. De multicore CPU heeft namelijk een gemeenschappelijk basis met daarop 2 of 4 uitvoerings eenheden. Alle cores kunnen bij alle instructie pipelines. Bij een dubbele processor is dat niet mogelijk en is synchronisatie complexer.
Als je met gemeenschappelijke basis de memory controller bedoelt, fair enough.
Verder is een multicore CPU niets meer als 4 aparte/losse CPUs op 1 package met nog wat glue logic. Soms zijn bepaalde caches geshared en zijn er nog wat extraatjes, maar het blijven X losse cores. Core1 kan dus niet bij de pipeline van core2 en andersom.
Omdat de CLR wel al support voor meerdere processors is in het 2.0 threading model in elk geval opgenomen dat subthreads altijd op dezelfde processor leven als de hoofd thread. Van wat ik heb begrepen is Microsoft's standpunt in deze dat zij nooit de interne werking kunnen garanderen (c.q. behouden) en daarom daarover ook geen uitspraken doen.
Dit geldt voor zover ik weet enkel voor COM en Co.
Ik weet dat Microsoft wel heeft geexperimenteerd met multicore support in de CLR. Het is niet bekend of deze ooit in 3.0 of 3.5 is toegevoegd.
Parallels extension for .NET ? Zie http://msdn.microsoft.com...rency/default(en-us).aspx
Overgens de AMD PDF raad nog steeds aan (sectie Lock Optimalisations) om locks (synchronisatie) te voorkomen zoals 'enter late, leave early' en 'lock only when necessary' en data te kopieren voor voordat de thread wordt gestart zodat subtreads geen (of minimaal) resources van de hoofd hoeven te raadplegen.
Dit is gewoon good practice en is voor elke programmeertaal en elk platform hetzelfde.
Overigens gaat ook de PDF over een multi core CPU. Net zoals de Intel processors delen alle processing units dezelfde basis en heeft de pipeline verplaatsing vooral een negatieve impact. Probleem schijnt de hardware abstractie van windows te zijn. Windows zal een AMD QuadCore processor zien als 4 processors en daardoor ziet de CLR ook 4 processors. Echter zijn deze virtueel omdat een OS geen taak aan een specifieke core kan hangen. Er is dus 1 queue met 4 consumers. Bij een dual quadcore systeem zijn er 2 queues met elk 4 consumers. En wat de wat STA mode in 2.0 doet is ervoor zorgen dat zoveel mogelijk threads op 1 processor komen zodat er geen processor synchronisatie nodig is.

Ik kom hier nog op terug.
Windows Vista heeft een nieuwe GetLogicalProcessorInformation() (zie http://msdn.microsoft.com/en-us/library/ms683194.aspx) die je kan gebruiken om informatie op te vragen over de processoren in het systeem.

Wat windows XP betreft, ziet die volgens mij toch echt wel 4 aparte processoren en is er dus niet 1 enkele runqueue voor meerdere processoren. Volgens mij sla je de bal mis met HyperThreading.

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 16-09 22:43
H!GHGuY schreef op donderdag 21 augustus 2008 @ 19:53:
Windows Vista heeft een nieuwe GetLogicalProcessorInformation() (zie http://msdn.microsoft.com/en-us/library/ms683194.aspx) die je kan gebruiken om informatie op te vragen over de processoren in het systeem.
Evenals Windows XP :)

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ik krijg ook een beetje een klop-en-klepel gevoel bij het verhaal van Niemand_Anders. Het grote voordeel van meerdere on-die cores is dat ze sneller met elkaar kunnen communiceren (er zit geen langzame bus tussen die over het moederbord moet). En afgezien van het feit dat ze wat resources kunnen sharen (zoals cache idd, maar ik zou dat niet meteen een voordeel willen noemen) is er geen verschil. De eerste Core 2 Quads van Intel waren zelfs feitelijk twee losse dual-cores in 1 package (geen idee of dat bij de nieuwere ook nog zo is trouwens).

Wat er bij Hyperthreading (Intels implementatie van SMT) gebeurt is dat er een enkele core is met een aantal resources (ALU's, instruction decoders, etc.) die worden gebruikt door meerdere hardware threads die naast elkaar lopen. Omdat ze die resources moeten delen is dat niet zo efficient als een multicore oplossing - twee number crunchers die tegelijk draaien zullen zo goed als geen winst halen bij een SMT core tov een normale core, omdat ze continu moeten vechten om dezelfde resources.

En dan komen we op het STA / MTA verhaal. In een STA COM applicatie kunnen op zich best meerdere threads draaien, echter draaien die altijd in hun eigen apartment met hun eigen resources (class instances). Er is geen resource sharing tussen apartments, en communicatie verloopt middels de thread message queue, en er moet dus ook een message pump zijn. Als je een COM object aanmaakt dat leeft in een andere apartment dan krijg je in feite een proxy naar dat object. Die proxy zorgt voor marshalling van de calls op dat object naar de thread van het daadwerkelijke object dmv de thread message queue.

Maar goed, dit komt dus uit de tijd van OLE en COM. COM GUI zut vereist een STA thread, en omdat Windows Forms COM gebruikt (drag & drop gaat bijv. middels OLE) heeft die dus ook een STA nodig. Wat niet meteen betekent dat je niet meerdere threads in je applicatie kunt gebruiken... Alleen COM marshalling zal ervoor zorgen dat je communicatie met dat soort objecten dus via de proxy verlopen (en dus niet in je eigen thread worden afgehandeld). .Net zelf heeft geen apartments en bouwt gewoon op het NT threading systeem wat prima met meerdere hardware threads, cores en processors om kan gaan. Synchronisatie gaat met de bestaande primitives, zoals kernel-level waitable objects (mutexes, semaphores, events) en de atomic bouwstenen voor user-level synchronisatie (zoals atomic compare-and-swap operaties, de Interlocked meuk dus).

De concurrency affairs articles van Jeffrey Richter zeggen verder ook verdacht weinig over thread scheduling in de CRL, noch over apartments.

[ Voor 8% gewijzigd door .oisyn op 22-08-2008 02:18 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • CMG
  • Registratie: Februari 2002
  • Laatst online: 10-12-2024

CMG

pedorus schreef op donderdag 17 juli 2008 @ 15:21:
Je start nu 2 threads in het voorbeeld, maar dat kan eigenlijk eentje minder door de hoofdthread ook een taak op zich te laten nemen, maar dat is een beetje een minioptimalisatie.
Ben ik niet helemaal met je eens, je wilt over het algemeen de hoofd thread voor UI Zaken responsive houden (weet niet of het in dit geval om een console app of GUI apje gaat).

NKCSS - Projects - YouTube


Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
CMG schreef op vrijdag 22 augustus 2008 @ 02:57:
[...]

Ben ik niet helemaal met je eens, je wilt over het algemeen de hoofd thread voor UI Zaken responsive houden (weet niet of het in dit geval om een console app of GUI apje gaat).
Een post onder die post wordt gevraag naar een progress-bar, waardoor die alinea sowieso onzin werd... :)

Misschien dat je bij veel threads daar maar het beste Interlocked voor kan gebruiken in plaats van Invoke(of Backgroundworker dat daar een klein laagje omheen is). Dan lees je gewoon om de zoveel tijd even de huidige toestand af. Aan de andere kant is precieze progress niet heel belangrijk, dus misschien dat het volatile keyword volstaat. MS gebruikt ook Interlocked zie ik nu, in combinatie met Backgroundworker. Ze gebruiken daar Parallel.For() om de boel automatisch op te splitsen. Ik denk enkel dat bij snelle processors de progress veel vaker dan nodig word geupdate in het voorbeeld in CTP june 2008.

Maar Parlellel.For() gebruiken lijkt me wel handig. In de toekomst gaan we naar duizenden cores, dus je kan maar beter voorbereid zijn :) Vanaf zo'n 6 processors treden nu vaak al behoorlijke problemen op als de synchronizatie niet handig gebeurd, dus 1000en wordt echt lastig... (4-6 processors is gebaseerd op ervaringen bij de cell processor)

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • HawVer
  • Registratie: Februari 2002
  • Laatst online: 13-09 16:51
Voor de mensen die zich wat dieper willen verdiepen in threads en .net, zie deze site:
http://www.albahari.com/threading/index.html Erg uitgebreid en zeer interessant.

http://hawvie.deviantart.com/


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ik zit het een beetje door te lezen, maar ik krijg erg m'n twijfels over de kennis en ervaring van de auteur.

Ik bedoel, neem dit voorbeeld:
Threads share data if they have a common reference to the same object instance. Here's an example:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ThreadTest {
 bool done;
 
 static void Main() {
   ThreadTest tt = new ThreadTest();   // Create a common instance
   new Thread (tt.Go).Start();
   tt.Go();
 }
 
 // Note that Go is now an instance method
 void Go() {
   if (!done) { done = true; Console.WriteLine ("Done"); }
 }
}

Because both threads call Go() on the same ThreadTest instance, they share the done field. This results in "Done" being printed once instead of twice
.
Bliep, race condition.
En vervolgens:
Static fields offer another way to share data between threads. Here's the same example with done as a static field:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
class ThreadTest {
 static bool done;    // Static fields are shared between all threads
 
 static void Main() {
   new Thread (Go).Start();
   Go();
 }
 
 static void Go() {
   if (!done) { done = true; Console.WriteLine ("Done"); }
 }
}

Both of these examples illustrate another key concept – that of thread safety (or, rather, lack of it!) The output is actually indeterminate: it's possible (although unlikely) that "Done" could be printed twice. If, however, we swap the order of statements in the Go method, then the odds of "Done" being printed twice go up dramatically:
C#:
1
2
3
static void Go() {
  if (!done) { Console.WriteLine ("Done"); done = true; }
}
Euh ja, dude, waarom denk je in hemelsnaam dat dat bij je vorige voorbeeld anders is?

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • HawVer
  • Registratie: Februari 2002
  • Laatst online: 13-09 16:51
.oisyn schreef op dinsdag 16 september 2008 @ 23:52:
Ik zit het een beetje door te lezen, maar ik krijg erg m'n twijfels over de kennis en ervaring van de auteur.

Ik bedoel, neem dit voorbeeld:

[...]
.
Bliep, race condition.
En vervolgens:

[...]

Euh ja, dude, waarom denk je in hemelsnaam dat dat bij je vorige voorbeeld anders is?
Hij zegt ook "Both of these examples" :P

http://hawvie.deviantart.com/


  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 22:26

Creepy

Tactical Espionage Splatterer

Hij zegt ook
Because both threads call Go() on the same ThreadTest instance, they share the done field. This results in "Done" being printed once instead of twice:
bij de eerste example zoals .oisyn al aanngeeft. En dat klopt gewoon niet. Hier kan prima done twee keer worden afgedrukt. Later begint ie over "both of these examples". Niet echt duidelijk verwoord...

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


  • HawVer
  • Registratie: Februari 2002
  • Laatst online: 13-09 16:51
Creepy schreef op woensdag 17 september 2008 @ 09:47:
Hij zegt ook

[...]

bij de eerste example zoals .oisyn al aanngeeft. En dat klopt gewoon niet. Hier kan prima done twee keer worden afgedrukt. Later begint ie over "both of these examples". Niet echt duidelijk verwoord...
Ik ben het met je eens dat hij het onduidelijk geformuleerd heeft. Wat hij bedoeld te zeggen is dat beide aanroepen op een andere thread uitgevoerd worden maar toch refereren naar hetzelfde veld done. Als zijn code thread safe was zou het voorbeeld beter uit de verf komen.

[ Voor 1% gewijzigd door HawVer op 17-09-2008 14:26 . Reden: typo's ]

http://hawvie.deviantart.com/


Verwijderd

nm,

Ik reageerde geloof ik op 2 pagina posts terug :).

[ Voor 80% gewijzigd door Verwijderd op 17-09-2008 11:29 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

HawVer schreef op woensdag 17 september 2008 @ 08:45:
[...]

Hij zegt ook "Both of these examples" :P
Mmmja, daar las ik idd overheen 8)7. Maar hij had het wat duidelijker kunnen formuleren :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1