Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / Managed C++ / March 2005

Tip: Looking for answers? Try searching our database.

Method calls in .NET multithreading

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Ioannis Vranos - 25 Mar 2005 04:58 GMT
In .NET multithreading we have to assign a thread to a method of a
separate object each time (and not two methods of the same object)?

In other words, why does this hung?

#using <mscorlib.dll>

using namespace System;
using namespace System::Threading;

class SomeException
{};

__gc class SomeClass
{
 int index;

 //...

 public:

 // ...

 void DoSomething()
 {
    Monitor::Enter(this);

    throw SomeException();

   // Modify index

    Monitor::Exit(this);
 }

 void DoSomethingElse()
 {
    Monitor::Enter(this);

    // Modify index

    Monitor::Exit(this);
  }

// ...
};

int main() try
{
    SomeClass *ps= __gc new SomeClass;

    Thread *pthread1= __gc new Thread ( __gc new ThreadStart(ps,
&SomeClass::DoSomething) );

    Thread *pthread2= __gc new Thread ( __gc new ThreadStart(ps,
&SomeClass::DoSomethingElse) );

    //Start execution of ps->DoSomething()
    pthread1->Start();

    //Start execution of ps->DoSomethingElse()
    pthread2->Start();
}

catch(SomeException)
{
    Console::WriteLine("SomeException caught in main thread!\n");
}
Ioannis Vranos - 25 Mar 2005 05:00 GMT
> In .NET multithreading we have to assign a thread to a method of a
> separate object each time (and not two methods of the same object)?

In other words, why does this hang?

I had pasted wrong code. Here is the correct one:

#using <mscorlib.dll>

using namespace System;
using namespace System::Threading;

class SomeException
{};

__gc class SomeClass
{
 int index;

 //...

 public:

 // ...

 void DoSomething() try
 {
    Monitor::Enter(this);

    throw SomeException();

   // Modify index

    Monitor::Exit(this);
 }

 ==> Handle inside the specific thread locally
 catch(SomeException)
 {
        // ...
 }

 void DoSomethingElse()
 {
    Monitor::Enter(this);

    // Modify index

    Monitor::Exit(this);
  }

// ...
};

int main() try
{
    SomeClass *ps= __gc new SomeClass;

    Thread *pthread1= __gc new Thread ( __gc new ThreadStart(ps,
&SomeClass::DoSomething) );

    Thread *pthread2= __gc new Thread ( __gc new ThreadStart(ps,
&SomeClass::DoSomethingElse) );

    //Start execution of ps->DoSomething()
    pthread1->Start();

    //Start execution of ps->DoSomethingElse()
    pthread2->Start();
}

catch(Exception *pe)
{
    Console::WriteLine("{0}", pe->Message);
}
Doug Harrison [MVP] - 25 Mar 2005 06:58 GMT
>> In .NET multithreading we have to assign a thread to a method of a
>> separate object each time (and not two methods of the same object)?
[quoted text clipped - 71 lines]
>      Console::WriteLine("{0}", pe->Message);
> }

I can't get this to hang.

You're starting two threads to run functions on the object ps, which is
fine in and of itself. However, the function DoSomething locks ps, throws
an exception, and apparently fails to unlock ps. Provided the thread goes
on to exit, I believe this will abandon the sync object associated with ps,
making it available to DoSomethingElse. If for some reason DoSomething
doesn't exit, then there's the potential for DoSomethingElse to wait
forever, and your program will hang. The solution to that is to unlock
"this" inside your catch block. (Of course, this all assumes the threads
execute in the order they're created.)

Signature

Doug Harrison
Microsoft MVP - Visual C++

Ioannis Vranos - 25 Mar 2005 07:20 GMT
> I can't get this to hang.

Strangely enough, it does not hang always. Run it sometimes and you will
see it hanging. I can not understand why it does not hang all the time
though (since Monitor::Exit() is not called in the catch block).

> You're starting two threads to run functions on the object ps, which is
> fine in and of itself. However, the function DoSomething locks ps, throws
[quoted text clipped - 5 lines]
> "this" inside your catch block. (Of course, this all assumes the threads
> execute in the order they're created.)

Yes, Monitor::Exit(this); inside the catch block makes the program to
never hang. However why it hangs sometimes and sometimes it doesn't,
without this?
Doug Harrison [MVP] - 25 Mar 2005 08:03 GMT
> Strangely enough, it does not hang always. Run it sometimes and you will
> see it hanging. I can not understand why it does not hang all the time
> though (since Monitor::Exit() is not called in the catch block).

FWIW. I ran it 20 times in a row, and it never hung. Aren't MT problems
fun?

> Yes, Monitor::Exit(this); inside the catch block makes the program to
> never hang. However why it hangs sometimes and sometimes it doesn't,
> without this?

It turns out I was wrong. Unlike a mutex, the SyncBlock associated with an
object is not abandoned when a thread exits while holding it. However, I
was on the right track when I alluded to order issues. Provided thread 1
runs before thread 2, locks the object, and exits, thread 2 will block
forever on its Monitor.Enter call. To make it do so (almost)
deterministically, insert the following at the start of DoSomethingElse:

    Thread::Sleep(1000);

This should give thread 1 enough time to do its thing and mess up thread 2.

Signature

Doug Harrison
Microsoft MVP - Visual C++

Ioannis Vranos - 25 Mar 2005 09:49 GMT
>>Strangely enough, it does not hang always. Run it sometimes and you will
>>see it hanging. I can not understand why it does not hang all the time
>>though (since Monitor::Exit() is not called in the catch block).
>
> FWIW. I ran it 20 times in a row, and it never hung. Aren't MT problems
> fun?

I compiled it from command line, with cl /clr /EHsc temp.cpp and no
other switch. /EHsc is important.

>>Yes, Monitor::Exit(this); inside the catch block makes the program to
>>never hang. However why it hangs sometimes and sometimes it doesn't,
[quoted text clipped - 10 lines]
>
> This should give thread 1 enough time to do its thing and mess up thread 2.

Does it hang to you with this way?  :-)
Doug Harrison [MVP] - 25 Mar 2005 17:38 GMT
>> To make it do so (almost)
>> deterministically, insert the following at the start of DoSomethingElse:
[quoted text clipped - 4 lines]
>
> Does it hang to you with this way?  :-)

Yes. I said "almost" deterministically because it's using Sleep to fake out
the scheduler, a practice that isn't guaranteed to work but does well
enough to demonstrate the problem.

Aside: It's interesting to note that by using the Thread class, your
example illustrated the use of foreground threads. Had you used background
threads, or if you were to set the IsBackground property on the threads,
your program would have terminated right away, because extant background
threads don't keep a program alive once the primary thread has exited. Only
foreground threads do that. In general, you should join with your secondary
threads before exiting your primary thread.

Signature

Doug Harrison
Microsoft MVP - Visual C++

Ioannis Vranos - 26 Mar 2005 03:34 GMT
> Yes. I said "almost" deterministically because it's using Sleep to fake out
> the scheduler, a practice that isn't guaranteed to work but does well
[quoted text clipped - 3 lines]
> example illustrated the use of foreground threads. Had you used background
> threads, or if you were to set the IsBackground property on the threads,

... the only way that I know for creating .NET background threads. Is
there another?

> your program would have terminated right away, because extant background
> threads don't keep a program alive once the primary thread has exited.

Yes indeed.

> Only
> foreground threads do that. In general, you should join with your secondary
> threads before exiting your primary thread.

You mean to join the primary thread and secondary threads all together?
However this doesn't sound much multithreading. :-)

Something else based on this, is it better to join dependent threads -
whenever possible- instead of using locks? What would this mean for
performance in multi-cpu/core environments? Is this a way to avoid the
thread lock mess, while sacrificing almost no speed?
Doug Harrison [MVP] - 26 Mar 2005 18:04 GMT
> ... the only way that I know for creating .NET background threads. Is
> there another?

Thread pool and by extension asynchronous delegates. In addition, unmanaged
threads that call into managed code are marked as background threads.

> You mean to join the primary thread and secondary threads all together?
> However this doesn't sound much multithreading. :-)

But if your program is shutting down, it's usually undesirable to allow
your secondary threads to continue to run as the larger environment in
which they're executiing is going away. To conduct an orderly shutdown,
your primary thread has to ask all your secondary threads to exit and join
with them first.

> Something else based on this, is it better to join dependent threads -
> whenever possible- instead of using locks?

Not sure what you mean. By "join" I mean wait for the thread to exit.

> What would this mean for
> performance in multi-cpu/core environments? Is this a way to avoid the
> thread lock mess, while sacrificing almost no speed?

Google on "lock-free programming". It's a hot topic these days.

Signature

Doug Harrison
Microsoft MVP - Visual C++

Carl Daniel [VC++ MVP] - 26 Mar 2005 22:12 GMT
>> Something else based on this, is it better to join dependent threads
>> - whenever possible- instead of using locks?
>
> Not sure what you mean. By "join" I mean wait for the thread to exit.

To the OP:

join is not rendezvous (in the Ada sense).  You can only join a thread when
it terminates (the pattern and terminology comes from Java most directly,
but dates to long before Java was born).

-cd
Jochen Kalmbach - 25 Mar 2005 10:23 GMT
Hi Ioannis,

>  void DoSomething()
>  {
[quoted text clipped - 6 lines]
>     Monitor::Exit(this);
>  }

The abouve code will always forget to call the "Exit" !

Normally you should *always* do:

Monitor::Enter(this);
__try
{
}
__finally
{
  Monitor::Exit(this);
}

--
Greetings
  Jochen

   My blog about Win32 and .NET
   http://blog.kalmbachnet.de/

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.