Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien
Toon posts:

[C#] Thread.Join bij 2 of meer threads

Pagina: 1
Acties:

Verwijderd

Topicstarter
Hoi,

Ik wil twee threads laten lopen (bovenop de main-thread, dus eigenlijk 3) maar dan die 2 extra threads wel voor een maximale tijd.

Nou dacht ik dat op te lossen door de threads te joinen met de parameter timeout zoals dit:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// create two threads and start them
List<MyThread> threads = new List<MyThread>();
foreach(MyPath path in paths)
{
   // thread is started within constructor of MyThread.
   MyThread thread = new MyThread(path);
   threads.Add(thread);
}

..
.. do other stuff in mainthread while two MyThreads are also running
..
.
timeout = 30000; // wait max 30 seconds
foreach (MyThread thread in threads)
{
    thread.Thread.Join(timeout);
}


dit heeft volgens mij als probleem dat als allebei de threads langer duren dan 30 seconden, je niet 30 seconden wacht maar 60 seconden. Hoe los ik dit goed op?

  • voodooless
  • Registratie: Januari 2002
  • Laatst online: 30-11 11:20

voodooless

Sound is no voodoo!

Check even wat join doet, dan wordt alles duidelijk.

Do diamonds shine on the dark side of the moon :?


  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Je zou beide threads een WaitHandle ( ManualResetEvent of AutoResetEvent ) kunnen geven en met WaitHandle.WaitAll op de threads kunnen wachten.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • prototype
  • Registratie: Juni 2001
  • Niet online

prototype

Cheer Bear

Volgens mij moet je meer in de richting denken van het interrupten van je threads. Dat moet wel een leuke hint zijn denk ik ;) Als je er niet uitkomt, heb ik nog hints genoeg :+

[ Voor 36% gewijzigd door prototype op 14-09-2007 14:51 ]


Verwijderd

Topicstarter
voodooless schreef op vrijdag 14 september 2007 @ 09:22:
Check even wat join doet, dan wordt alles duidelijk.
Nou, "blocks the calling thread untill a thread terminates".

En dat veroorzaakt precies het probleem wat ik probeer uit te leggen als ik een foreach loop gebruik, toch?

Even een voorbeeld voor de duidelijkheid.

Stel dat Thread1 er altijd 10 seconden over doet en thread2 35 (of langer), dan wil ik eigenlijk dat thread2 een timeout oplevert want ik wil dat het totaal er max 30 over doet.

Maar in de foreach gebeurt er volgens mij iets anders: eerst komt de join voor thread1 langs, die wacht dus 10 seconden. Dan wordt voor de thread2 weer een nieuwe join(block) aangeroepen met een (nieuwe) timeout van 30 seconden.... als thread2 er dan 40 (of minder) seconden over doet dan is die nog op tijd. Maar ik wil dat thread2 er max 30 over mag doen.
Zie je het probleem? Of begrijp ik iets verkeerd?

  • prototype
  • Registratie: Juni 2001
  • Niet online

prototype

Cheer Bear

Verwijderd schreef op woensdag 19 september 2007 @ 09:19:
[...]


Nou, "blocks the calling thread untill a thread terminates".

En dat veroorzaakt precies het probleem wat ik probeer uit te leggen als ik een foreach loop gebruik, toch?

Even een voorbeeld voor de duidelijkheid.

Stel dat Thread1 er altijd 10 seconden over doet en thread2 35 (of langer), dan wil ik eigenlijk dat thread2 een timeout oplevert want ik wil dat het totaal er max 30 over doet.

Maar in de foreach gebeurt er volgens mij iets anders: eerst komt de join voor thread1 langs, die wacht dus 10 seconden. Dan wordt voor de thread2 weer een nieuwe join(block) aangeroepen met een (nieuwe) timeout van 30 seconden.... als thread2 er dan 40 (of minder) seconden over doet dan is die nog op tijd. Maar ik wil dat thread2 er max 30 over mag doen.
Zie je het probleem? Of begrijp ik iets verkeerd?
Ja, dat is wat join() doet, maar je roept hier de join(int) overload aan, en die doet volgens MSDN:
Blocks the calling thread until a thread terminates or the specified time elapses, while continuing to perform standard COM and SendMessage pumping.
Wat dit dus inhoudt is dat je calling thread bij join(x) welliswaar blocked voor x tijdeenheden, maar het niet per definitie zo hoeft te zijn dat die thread waar hij op zat te wachten ook daadwerkelijk geterminate is. Let hierbij op dus dat join(Timeout.Infinite) equivalent dus is aan join(). Dit is niet een manier dus om threads te onderbreken zoals jij w.s. wil, maar je zult denk ik toch in de richting van interrupts moeten denken. Eventueel bieden constructies met conditionele variabelen ook wel uitkomst binnen de threads waar je op zit te wachten.

Wat betreft het probleem in je startpost, dat volgt dus ook uit mijn beschrijving. Het feit namelijk dat je de joins door een bepaalde thread laat aanroepen zorgt ervoor dat deze thread er ten hoogste de som van deze joins op moet wachten, maar wederom is dat nog niet een garantie dat de threads waar hij op zat te wachten ook daadwerkelijk geterminate zijn.

Verwijderd

Topicstarter
prototype schreef op woensdag 19 september 2007 @ 09:44:
[...]

Ja, dat is wat join() doet, maar je roept hier de join(int) overload aan, en die doet volgens MSDN:

[...]

Wat dit dus inhoudt is dat je calling thread bij join(x) welliswaar blocked voor x tijdeenheden, maar het niet per definitie zo hoeft te zijn dat die thread waar hij op zat te wachten ook daadwerkelijk geterminate is. Let hierbij op dus dat join(Timeout.Infinite) equivalent dus is aan join(). Dit is niet een manier dus om threads te onderbreken zoals jij w.s. wil, maar je zult denk ik toch in de richting van interrupts moeten denken. Eventueel bieden constructies met conditionele variabelen ook wel uitkomst binnen de threads waar je op zit te wachten.

Wat betreft het probleem in je startpost, dat volgt dus ook uit mijn beschrijving. Het feit namelijk dat je de joins door een bepaalde thread laat aanroepen zorgt ervoor dat deze thread er ten hoogste de som van deze joins op moet wachten, maar wederom is dat nog niet een garantie dat de threads waar hij op zat te wachten ook daadwerkelijk geterminate zijn.
Maar dat laatste hoeft van mij ook niet.
Dan heb ik misschien niet helemaal duidelijk uitgelegd wat ik wil.

Wat ik wil bereiken is:

Ga je gang met thread1 en thread2, en ik ben tevreden met drie dingen:
- resultaten uit zowel thread1 als thread2, zolang dat totaal max 30 sec duurt (dit zou het optimale geval zijn)
- resultaten uit alleen thread1 (en als thread2 er langer dan 30 sec over doet, wil ik die resultaten ignoren want ik wil er niet meer op wachten)
- resultaten uit alleen thread2 (en als thread1 er langer dan 30 sec over doet, wil ik die resultaten ignoren want ik wil er niet meer op wachten)

Dus ik hoef niet per se te wachten op beide threads. Het is dus niet zo dat als 1 van de twee een timeout heeft, ik niet verder kan en exception wil gooien ofzo. Ben met 1 resultaat uit 1 van beide threads tevreden.

Ik denk dat ik inderdaad iets met WaitHandle.WaitAll(waitHandles, timeout, ...) moet doen om te zorgen dat ik die timeout zo in kan stellen als ik wil.

  • voodooless
  • Registratie: Januari 2002
  • Laatst online: 30-11 11:20

voodooless

Sound is no voodoo!

Waarom dan join gebruiken? Wacht gewoon 30 seconden, en check dan of een van de twee thread, of beiden resultaat opleveren. Of nog beter, check al eerder of de threads resultaten leveren (en wacht maximaal 30 seconden op twee resultaten.

Het mooiste is natuurlijk om dit soort dingen zonder polling to doen :)

Do diamonds shine on the dark side of the moon :?


Verwijderd

Topicstarter
voodooless schreef op woensdag 19 september 2007 @ 09:59:
Waarom dan join gebruiken? Wacht gewoon 30 seconden, en check dan of een van de twee thread, of beiden resultaat opleveren. Of nog beter, check al eerder of de threads resultaten leveren (en wacht maximaal 30 seconden op twee resultaten.

Het mooiste is natuurlijk om dit soort dingen zonder polling to doen :)
Ja, en hoe doe ik dat dan? Misschien zie ik iets heel simpels over het hoofd? :)

Als ik zoek kom ik toch uit op polling met IAsyncResult zoals beschreven op:
http://msdn2.microsoft.com/en-us/library/2e08f6yc.aspx

Gaat me wat ver maar ben bang dat het de enige mogelijkheid is.

[ Voor 16% gewijzigd door Verwijderd op 19-09-2007 10:39 ]


  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 31-10 11:58
Een thread starten van waaruit je 2 andere threads start, deze eerste thread joinen met een timeout van 30 seconden en daarna controleren of de twee inner threads hun waarde gevuld hebben? Kan je ook gelijk vanuit die eerste thread de inner threads terminaten als z'n timeout verstreken is. Want daar moet je denk ik ook rekening mee houden.

Edit:
Gaat denk ik ook niet direct werken, omdat Thread 1 natuurlijk direct terugkomt nadat ie z'n twee inner threads gestart heeft...

Doet dit niet wat je wilt:
using System;
using System.Threading;

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
46
47
48
49
50
51
namespace threading
{
    class Program
    {
        static int i1, i2;

        static void Main(string[] args)
        {
            Thread t = new Thread(foo1);
            t.Start();

            t.Join(5000);
            t.Abort();

            Console.WriteLine(i1 + ", " + i2);
        }

        private static void foo1()
        {
            Thread t1 = new Thread(foo2);
            Thread t2 = new Thread(foo3);

            try
            {
                t1.Start();
                t2.Start();

                t1.Join();
                t2.Join();
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex);
            }

            t1.Abort();
            t2.Abort();
        }

        private static void foo2()
        {
            Thread.Sleep(30000);
            i1 = 2;
        }

        private static void foo3()
        {
            i2 = 3;
        }
    }
}


De foo1 blijft wachten tot alle threads die hij start afgelopen zijn, maar de main blijft hooguit 5sec wachten tot foo1 klaar is. Omdat in foo2 een sleep zit wordt deze niet uitgevoerd, en zal i1 ook geen waarde bevatten.

[ Voor 73% gewijzigd door riezebosch op 19-09-2007 11:24 ]

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 30-11 00:17
Die WaitAll doet toch precies wat je wilt? Hij wacht op beide threads ( maximaal 30 seconden ) , en komt dan met true als beide klaar waren of false als er 1 of meer niet klaar was/waren.

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.


Verwijderd

Topicstarter
De oplossing van Riezebosch spreekt me wel aan. Thanks!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 30-11 00:17
Als de WaitAll doet wat je wilt lijkt me die oplossing een behoorlijk stuk meer ingewikkeld dan nodig is tbh

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.


  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 31-10 11:58
Ik zat ook nu al naar de WaitAll te kijken. Als dat inderdaad doet wat je wilt zou ik ook gebruik maken van de functionaliteit uit het framework. Hoewel ik niet direct helder krijg hoe je het daarmee op zou moeten zetten.

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


Verwijderd

Topicstarter
De opzet die ik nu heb is dat MyThread een eigen class is, en in de constructor doe ik (in feite) de thread.Start(), dus is die oplossing van riezebosch nu makkelijker te implementeren dan WaitAll.

Al vraag ik me wel af, of de thread1 en thread2 goed ge-abort worden.
Prototype waarschuwt daar al voor: je kunt die "hoofdthread" wel leuk aborten, maar zijn de (onderliggende) threads 1 en 2 dan ook automatisch ge-abort? Ik vrees van niet, moet ik nog effe gaan uittesten, anders moeten die t1.abort en t2.abort misschien ergens in een finally gezet worden.

Ik heb ook even zitten kijken of ik de WaitAll zou kunnen implementeren maar dan moet ik de MyThread classen veranderen dat die gebruik maakt van de IAsync Interface, met een delegate, en om een aantal redenen die in de MyThread class zitten, zie ik dat nu effe niet zitten :)

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 30-11 00:17
Ik heb natuurlijk geen inzicht in jou MyThread ding maar ik snap niet hoe je bij die IAsync interface komt?

Klein probeerseltje van mijn kant, Laat je niet afschrikken door het lawaai, de uiteindelijke synchronisatie is vrij voor de hand liggend.

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
using System;
using System.Threading;


class MyThreadStarter
{
    private Thread          m_Thread;
    private EventWaitHandle m_Handle;
    private int             m_SleepTime;

    public MyThreadStarter( int SleepTime )
    {
        m_SleepTime = SleepTime;

        m_Handle = new EventWaitHandle( true, EventResetMode.ManualReset );
        m_Thread = new Thread(new ThreadStart(this.ThreadProc));
    }

    public WaitHandle Handle
    {
        get { return m_Handle; }
    }

    public void Start( )
    {
        m_Handle.Reset();
        m_Thread.Start();
    }

    public bool Stop()
    {
        if( m_Thread.ThreadState == ThreadState.Unstarted )
            return true;

        if( !m_Thread.Join( 1000 ) )
        {   
            Console.WriteLine( "ABORTING {0}", m_SleepTime );
            m_Thread.Abort( );
            if( !m_Thread.Join( 1000 ) )
                return false;
            else
                return true;
        }
        return true;
    }

    public void ThreadProc()
    {
        Console.WriteLine( "START {0}", m_SleepTime );
        try
        {
            Thread.Sleep( m_SleepTime * 1000 );
        }
        catch( ThreadAbortException Exc )
        {
            Console.WriteLine( "ABORT {0} : {1}", m_SleepTime, Exc.Message  );
        }
        finally
        {
            Console.WriteLine( "END {0}", m_SleepTime );
            m_Handle.Set( );
        }
        
    }
}

namespace ThreadTest
{
    class Program
    {
        static void Main( string[] args )
        {
            MyThreadStarter T1 = new MyThreadStarter( 5 );
            MyThreadStarter T2 = new MyThreadStarter( 30 );

            WaitHandle[] Handles = { T1.Handle, T2.Handle };

            T1.Start();
            T2.Start();

            DateTime StartTs = DateTime.Now;

            if( !WaitHandle.WaitAll( Handles, 8 * 1000, false ) )
                Console.WriteLine( "WAIT TIMEOUT {0}", (DateTime.Now-StartTs) );
            else
                Console.WriteLine( "THREADS ENDED {0}", DateTime.Now-StartTs );

            if( !T1.Stop() || !T2.Stop() )
                Console.WriteLine( "A THREAD IS BLOCKED" );

            Console.ReadLine();
        }
    }
}

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.


  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 31-10 11:58
farlane schreef op donderdag 20 september 2007 @ 13:46:
Ik heb natuurlijk geen inzicht in jou MyThread ding maar ik snap niet hoe je bij die IAsync interface komt?

Klein probeerseltje van mijn kant, Laat je niet afschrikken door het lawaai, de uiteindelijke synchronisatie is vrij voor de hand liggend.

[...]
Ziet er goed uit, farlane! Maar waarom regel 39? Uiteindelijk doet deze code volgens mij helemaal niet zoveel anders dan mijn voorbeeldje, alleen maak je iets meer gebruik van het Framework :)

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 30-11 00:17
riezebosch schreef op donderdag 20 september 2007 @ 14:27:
Ziet er goed uit, farlane! Maar waarom regel 39?
Omdat ik de thread ook na een abort graag wil joinen.
Uiteindelijk doet deze code volgens mij helemaal niet zoveel anders dan mijn voorbeeldje, alleen maak je iets meer gebruik van het Framework :)
Nja, je maakt een thread aan speciaal om de andere twee te starten terwijl dat imho niet nodig is.

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.


Verwijderd

Topicstarter
farlane schreef op donderdag 20 september 2007 @ 13:46:
Ik heb natuurlijk geen inzicht in jou MyThread ding maar ik snap niet hoe je bij die IAsync interface komt?
Leuk stukje code: ik wist niet dat je die WaitEvent op die manier zelf in het leven kan roepen. Nou krijg ik de indruk dat die IAsyncResult hetzelfde doet. Zie voorbeeld dat staat op http://msdn2.microsoft.co...rary/ms228963(VS.80).aspx
Pagina: 1