multithreading.net a practical perspective peter ibbotson

26
Multithreading .NET a practical perspective Peter Ibbotson

Upload: anna-oliver

Post on 27-Dec-2015

228 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: Multithreading.NET a practical perspective Peter Ibbotson

Multithreading .NET a practical perspective

Peter Ibbotson

Page 2: Multithreading.NET a practical perspective Peter Ibbotson

Why multi-thread?

With new dual core processors makes better use of the CPU

Keeps the User Interface responsive during long calculations.

Allows Asynchrous IO - Asynchronous Programming model (aka APM)

Single CPU systems can be slightly slower, but the user experience is better.

Page 3: Multithreading.NET a practical perspective Peter Ibbotson

Why not multi-thread?

It's hard to do right. Really it is! Generates Heisenbugs very easily Synchronisation is hard Often you don't really need it Talking to Windows forms controls is

tricky Threads aren't cheap

Page 4: Multithreading.NET a practical perspective Peter Ibbotson

Thread basics I

Threads aren't "free" They get 1MB of address reserved for

stack + 12KB of kernel-mode stack Each DLL in the parent process gets called

to say a new thread has been created Each DLL also gets called when a thread is

about to die. Context switches save / restore registers

(700 bytes on x86, 1240 on x64) and use a spinlock so take time.

Page 5: Multithreading.NET a practical perspective Peter Ibbotson

Thread basics II

Create a tread using ThreadStart passing in a delegate to call:

Thread Worker=new Thread(new ThreadStart(StartMethod));

Start the thread using Thread.Start(); Stop a thread using Thread.Abort() Suspend a thread using

Thread.Suspend(); Wait for a thread to complete using

Thread.Join();

Page 6: Multithreading.NET a practical perspective Peter Ibbotson

Example

NotesParameterised threads aren't always a

good idea particularly as they aren't type safe.

Often better to create a whole class with local variables that you initialise before you start the thread.

Page 7: Multithreading.NET a practical perspective Peter Ibbotson

Variables and caches

Two threads share variables BUT the cache gets in the way. Each one may not see the same value.

internal sealed class CacheCoherencyProblem{ private byte m_init=0; private Int32 m_value=0; public void Thread1(){ m_value=5; m_init=1;} public void Thread2(){ if (m_init==1)

WriteLine(m_value);}}

In theory if you run this it may display 0, but this depends on cachelines

Page 8: Multithreading.NET a practical perspective Peter Ibbotson

Variables and caches step 1

CPU1 runs Thread1 CPU2 Thread2 internal sealed class CacheCoherencyProblem

{ private byte m_init=0; private Int32 m_value=0; public void Thread1(){ m_value=5; m_init=1;} public void Thread2(){ if (m_init==1) WriteLine(m_value);}}

CPU2 reads a byte from memory and it's before m_value fields bytes in memory, so both of them get read into cache.

So CPU2 has m_value already in cache

Page 9: Multithreading.NET a practical perspective Peter Ibbotson

Variables and caches step 2

CPU1 runs Thread1 CPU2 Thread2 internal sealed class CacheCoherencyProblem

{ private byte m_init=0; private Int32 m_value=0; public void Thread1(){ m_value=5; m_init=1;} public void Thread2(){ if (m_init==1)

WriteLine(m_value);}}

CPU1 runs Thread1 and changes m_value to 5 but this stays in cache, the following m_init=1 line gets written straight out to main memory

Page 10: Multithreading.NET a practical perspective Peter Ibbotson

Variables and caches step 3

CPU1 runs Thread1 CPU2 Thread2 internal sealed class CacheCoherencyProblem

{ private byte m_init=0; private Int32 m_value=0; public void Thread1(){ m_value=5; m_init=1;} public void Thread2(){ if (m_init==1)

WriteLine(m_value);}}

Now CPU2 reads in m_init sees it's set to 1 and reuses the m_value from cache so m_value contains 0 and that is whats displayed

Page 11: Multithreading.NET a practical perspective Peter Ibbotson

Sorry No demonstration

Sadly I couldn't get something like this to fail reliably

There are work arounds Use VolatileRead & Write Better yet use the C# keyword volatile. Or just ignore it and use either

System.Threading.InterlockedMonitor class and C# lock

Page 12: Multithreading.NET a practical perspective Peter Ibbotson

The better idea use the monitor class

Quick demo of the problemExample 2

Page 13: Multithreading.NET a practical perspective Peter Ibbotson

The better idea use the monitor class

Quick demo of the problemExample 2

Hopefully that race condition printed bingo a few times

The solution is to use C# lock statementBack to the example

Page 14: Multithreading.NET a practical perspective Peter Ibbotson

Monitor class and lock statement

private void SomeMethod(){ lock(this) { …. Access object }}

Private void SomeMethod()

{ Object temp=this;

Monitor.enter(temp);

try

{

….Access object

}

finally

{

Monitor.Exit(temp); }

Page 15: Multithreading.NET a practical perspective Peter Ibbotson

Using locks in the real world

Don't do too many locks (Prefer locking larger objects)

Beware of nesting too deeply Beware of deadlocking.

Thread A acquires locks in this order Lock 1,2. Thread B acquires locks in this order Lock 2,1. On a bad day thread A has 1 locked and is waiting for 2 to become free.This will never happen as Thread B has lock 2 and is waiting for lock 1

Page 16: Multithreading.NET a practical perspective Peter Ibbotson

Reader writer locks

ReaderWriterLock gives many readers access but only one writer.

Vance Morrison has more on this from the performance point of view

Use his version if performance is a problem from:

http://blogs.msdn.com/vancem/archive/2006/03/28/563180.aspx

Page 17: Multithreading.NET a practical perspective Peter Ibbotson

Thread pool

After all that don't create threads directly. They are quite expensive.

Use System.Threading.Threadpool instead. The thread pool puts threads into two

classesCompute bound IO bound

It adds threads to the pool in 500ms increments – See example (3)

Page 18: Multithreading.NET a practical perspective Peter Ibbotson

Asynchronous Programming Model

It's a pattern for doing asynchronous callbacks when something completes (i.e. network, file io etc)

Always of the form BeginXxxx EndXxxxBeginRead / EndRead for filesBeginConnect / EndConnect for socketsEtc…

Always Call EndXxxx otherwise you'll leak The callback is on a ThreadPool thread. Time for an example (4)

Page 19: Multithreading.NET a practical perspective Peter Ibbotson

Windows forms

An alternative to multi-threading Use a modal dialog Call DoEvents in loop while pooling

the cancel button state. Quick example (5)

Page 20: Multithreading.NET a practical perspective Peter Ibbotson

Windows forms some VB6 tricks revisited

An alternative to multi-threading Use a modal dialog Call DoEvents in loop while pooling the

cancel button state. Quick example (5) Other alternatives - use a WinForms

timer and a state machine. The WinForms timer posts WM_TIMER messages on the main UI thread.

Page 21: Multithreading.NET a practical perspective Peter Ibbotson

Sometimes even Microsoft go single threaded Version 6 of the animation control stopped

using a background worker thread now uses WM_TIMER

If the main window stops reading messages (DoEvents) there is a chance the animation control will stall trying to get the background colour

If the foreground process doesn't read messages then if another window sends a broadcast message, windows stops but the animation keeps running.

See http://blogs.msdn.com/oldnewthing/archive/2006/03/16/552821.aspx

Page 22: Multithreading.NET a practical perspective Peter Ibbotson

Windows forms and threads(Examples stolen from chris sells)

"Thou shalt not operate on a window from other than its creating thread”

This means you need to use Invoke orBeginInvoke and EndInvoke

These transfer control back to the UI thread.

http://weblogs.asp.net/justin_rogers/articles/126345.aspx has a load of details

Example 6 and 7

Page 23: Multithreading.NET a practical perspective Peter Ibbotson

Windows forms and threads(Examples stolen from chris sells)

"Thou shalt not operate on a window from other than its creating thread”

This means you need to use Invoke orBeginInvoke and EndInvoke

These transfer control back to the UI thread.

Adding a Cancel button Example 8

Page 24: Multithreading.NET a practical perspective Peter Ibbotson

ComponentModel.BackgroundWorker

Use this for simple background tasks Simple to use and doesn't require

BeginInvoke and friends

Page 25: Multithreading.NET a practical perspective Peter Ibbotson

Multiple UI threads

Useful for things like reports Use Application.Run from a new

thread.

Page 26: Multithreading.NET a practical perspective Peter Ibbotson

Further resources

Chris Sells MSDN articles http://msdn.microsoft.com/library/en-us/dnforms/html/winforms06112002.asp

Jeffrey Richter – CLR via C# (book) Vance Morrison

http://msdn.microsoft.com/msdnmag/issues/05/08/Concurrency/default.aspx

Invoke and friends http://weblogs.asp.net/justin_rogers/articles/126345.aspx has a load of details

This powerpoint is at http://www.ibbotson.co.uk/articles