advanced language concepts in c# david figge [email protected] session 3 last update: 3/09page...
Post on 20-Dec-2015
219 views
TRANSCRIPT
Copyright (C) 2009 by David Figge. All Rights Reserved.
Advanced Language Concepts in C#
David [email protected]
Session 3
Last Update: 3/09 Page 1
Copyright (C) 2009 by David Figge. All Rights Reserved.
Advanced Language Concepts in C#
Where We’re at, Where We’re Going…
Last Update: 3/09 Page 2
Copyright (C) 2009 by David Figge. All Rights Reserved.
Session 1: Review, Introductions Language INtegrated Query
(LINQ) Session 2:
More Linq (continued) Session 3:
Still More Linq (continued) Session 4:
Creating tables in a Database Attributes
Session 5: Relationships and Referential
Integrity Session 6:
Displaying XML data with DataGrid
Nullable Types SQL Security Concepts .Net Security
Session 7: Class Libraries Namespaces and Assemblies Versioning Global Assembly Cache (GAC) Extern Aliases
Session 8: Multi-Threading
Session 9: Anonymous Methods Integrating Help Localization Windows Setup/Click-Once
Deployment Session 10:
Final Project
Class 3 Schedule
Last Update: 3/09 Page 3
Copyright (C) 2009 by David Figge. All Rights Reserved.
Advanced Language Concepts in C#
Working with Class Libraries, Namespaces, and
Assemblies
Last Update: 3/09 Page 4
Copyright (C) 2009 by David Figge. All Rights Reserved.
Assemblies
In .Net, a deployable unit of code is called an Assembly
Typically, it consists of one module, created by compiling one or more source files into an .exe or .dll file
We’ve been building our Address program into a single module assembly called address.exe
.Net does support multi-module assemblies I’m not going to go into that here
Visual Studio doesn’t support it So you’d have to use to command-line compiler with options
It’s not commonly needed Instructions to do this are in the book
Let's see more about assemblies…
Last Update: 3/09 Page 5
Copyright (C) 2009 by David Figge. All Rights Reserved.
Assembly Characteristics
A binary file hosted by the CLR Although they use the standard executable file format
for Windows, it loads the CLR first, then passes to the CLR the assembly start point
Typically has either an .exe or a .dll extension But can also have other extensions
Establish a type boundary So if two assemblies define the same type, each is seen
as unique to its assembly Support version numbers
The format is <major>.<minor>.<build>.<revision> Along with an optional public key (“strongly named”),
this allows multiple versions of the same assembly to coexist on the machine
Last Update: 3/09 Page 6
Copyright (C) 2009 by David Figge. All Rights Reserved.
Assembly Characteristics
Self Describing This means two things
All assemblies used by this one are specifically referenced so they can be loaded as the program starts
Although you can manually load assemblies at runtime outside of this
The assembly has metadata describing the functions in it
You can access this information through the Reflection API
Configurable Can be deployed as private or shared
More on this later Config files can also be used to define locations
and loading options
Last Update: 3/09 Page 7
Copyright (C) 2009 by David Figge. All Rights Reserved.
Namespaces
When projects get large or use multiple assemblies, the potential for naming conflicts increases
To help manage these conflicts, Namespaces are used to identify groups of classes that go together Typically, by the source file or assembly they're
in Namespaces are defined using the
Namespace keyword You can define new namespaces at any time you
see a value Let's see an example of Namespaces…
Last Update: 3/09 Page 8
Copyright (C) 2009 by David Figge. All Rights Reserved.
Code
Example Example Namespace
namespace MyShapes{
class Circle { ... }class Triangle { ... }class Square { ... }
}
// Using the namespace (separate program...)using System;using MyShapes;namespace MyApp{
class MyAppClass {static void Main(string[] args){
Circle c = new Circle(); // Or = new MyShapes.Circle()Square s = new Square(); // fully qualified can avoidTriangle t = new Triangle(); // namespace clashes
}}
}
Last Update: 3/09 Page 9
So here we have an example of the MyShapes namespace defined in, say, an
add-on DLL.To use the namespace in our application, we simply put a using statement (you may need to add a
reference if the namespace is in another assembly).
Note the alternative to including the using statement would be to fully
reference Circle, like
Circle c = new MyShapes.Circle();
Using a fully qualified path can avoid namespace clashes
Making senseso far?
Copyright (C) 2009 by David Figge. All Rights Reserved.
Aliases
C# also supports the use of aliases These can come in handy when you
have two namespaces that have similar functionality
Let's look at an example…
Last Update: 3/09 Page 10
Copyright (C) 2009 by David Figge. All Rights Reserved.
Code
Example Alias Example
// In Bob.dll, namespace Bob, who works in the CoolVisuals product division at BigCorpnamespace Graphics{
class Circle { ... } // Bob’s Circle function is really fastclass Triangle { ... }class Square { ... }
}// In Sue.dll, namespace Sue. Sue works at SmallShop in competition with BigCorp and Bobnamespace Graphics{
class Circle { ... }class Triangle { ... } // But Sue’s triangle function beats Bob’s every timeclass Square { ... }
}
// separate program...using System;using BigCorp.CoolVisuals.Bob.Graphics;using SmallShop.AlsoCoolVisuals.Sue.Graphics;namespace MyApp{
class MyAppClass {static void Main(string[] args){
Circle c = new Circle(); // Ambiguous. C# doesn’t know which you mean!Triangle t = new Triangle();
}}
}
Last Update: 3/09 Page 11
So Bob, who works for the CoolVisuals group at BigCorp has a set
of graphics functions. They aren’t spectacular, except for his Circle
function, which is really fast.
Sue, who works has a product AlsoCoolVisuals, published by
SmallShop. Sue’s Triangle function is much better than Bob’s.
So now we’re ready to use Bob’s circle and Sue’s Triangle. But there’s a problem. Since we included both using
statements, there are now two circle and two triangle functions (not to mention the squares!). We could fully
qualify all our uses, but that would be awkward…
Circle c = new BigCorp.CoolVisuals.Bob.Graphics.Circle()
This is where aliases come in…
Copyright (C) 2009 by David Figge. All Rights Reserved.
Code
Example Alias Example
// In Bob.dll, namespace Bob, who works in the CoolVisuals product division at BigCorpnamespace Graphics{
class Circle { ... } // Bob’s Circle function is really fastclass Triangle { ... }class Square { ... }
}// In Sue.dll, namespace Sue. Sue works at SmallShop in competition with BigCorp and Bobnamespace Graphics{
class Circle { ... }class Triangle { ... } // But Sue’s triangle function beats Bob’s every timeclass Square { ... }
}
// separate program...using System;using bobs = BigCorp.CoolVisuals.Bob.Graphics;using sues = SmallShop.AlsoCoolVisuals.Sue.Graphics;namespace MyApp{
class MyAppClass {static void Main(string[] args){
Circle c = new bobs.Circle(); // Uses Bob.GraphicsTriangle t = new sues.Triangle(); // Uses Sue.Graphics
}}
}
Last Update: 3/09 Page 12
This is the syntax for an Alias. It allows you to define an identifier that is a
placeholder for a namespace.
By using the alias, the namespaces are clear, and the code is easy to read.
Questionson that?
Copyright (C) 2009 by David Figge. All Rights Reserved.
Namespaces
You can also nest namespaces By default, VS creates a namespace
for your app Same as the name of your app Change this using the Default
Namespace setting in the Application tab of the project properties.
Last Update: 3/09 Page 13
Copyright (C) 2009 by David Figge. All Rights Reserved.
Applications vs. Class Libraries
An application consists of A collection of functions
One of which must be Main Resources (memory, etc) An execution context (a “thread”)
This includes a stack A class library (aka code library) consists of
A collection of functions Resources (optional)
So a class library is simply a collection of functions available to other programs
It uses the calling program’s context for execution
i.e. its memory space, its “thread space”, etcLast Update: 3/09 Page 14
Copyright (C) 2009 by David Figge. All Rights Reserved.
Let’s Try It…
Open up your Address program Create a new project (part of the
solution) Type is a Class Library
So you'll be able to use this for other apps as well
Call it IUtils, for Internet Utilities In it, put this code…
Last Update: 3/09 Page 15
Copyright (C) 2009 by David Figge. All Rights Reserved.
Code
Example Locate Function
namespace IUtils{ public class Maps { public static void Lookup(string address, string csz) { string combined = address + ", " + csz; // Add+CSZ string converted = combined.Replace(' ', '+'); // repl sp with + string args = "maps.google.com/maps?q=" + converted;// Full URL
// Fire off IE as a new process System.Diagnostics.Process proc = new System.Diagnostics.Process(); proc.EnableRaisingEvents = false; proc.StartInfo.FileName = "iexplore.exe"; proc.StartInfo.Arguments = args; proc.StartInfo.UseShellExecute = true; proc.Start(); } }}
Last Update: 3/09 Page 16
Copyright (C) 2009 by David Figge. All Rights Reserved.
Using IUtils from Address
Make sure IUtils builds Add a reference to IUtils from
Address Add a new button to the Address
form Call it btnMap In it, we will call the Map.Locate
function in IUtils Here’s the code…
Last Update: 3/09 Page 17
Copyright (C) 2009 by David Figge. All Rights Reserved.
Code
Example Calling the Locate Function
private void btnMap_Click(object sender, EventArgs e){ Entry ent = SelectedEntries[current]; IUtils.Maps.Lookup(ent.Address, ent.CSZ);}
Last Update: 3/09 Page 18Type it in. It should work…
Copyright (C) 2009 by David Figge. All Rights Reserved.
Private Assemblies
The IUtils DLL is a private assembly It’s required to be in the same directory as the exe
You can dynamically load it (i.e. as it is running) from any location
You can also use another location using a config file (in this case, IUtils.config) to specify location
the book has more on this
You can see it there now By defining a reference to it, the DLL gets placed in
there .Net does not use the registry when searching for DLLs Copying to another location is easy (unlike COM)
copy all files in the directory Private assemblies don’t specifically use version
numbers, as this is the only app that uses the DLL where it is
Last Update: 3/09 Page 19
Copyright (C) 2009 by David Figge. All Rights Reserved.
Shared Assemblies
Like private assemblies, shared assemblies contain functions and (optionally) data that programs can use
Shared assemblies, however, are available to all .Net apps that want to use them. The .Net framework assemblies are, for
example, shared assemblies So, what has to happen to make a
shared assembly?Last Update: 3/09 Page 20
Copyright (C) 2009 by David Figge. All Rights Reserved.
Shared Assemblies
Shared Assemblies are placed in a commonly accessible location on your machine called the Global Assembly Cache, or the GAC Located at \windows\assembly Note that only *.dll assemblies can be stored in
the GAC (not even *.exe) Shared assemblies must have Strong
Names This uniquely identifies the publisher of the
assembly This could be a developer, a department, or a
company as a wholeLast Update: 3/09 Page 21
Copyright (C) 2009 by David Figge. All Rights Reserved.
Strong Names
The strong name creates a unique identifier for the assembly
Similar to the GUID for COM objects Strong names make use of public key/private key
encryption Formally, they consist of
The friendly name of the assembly The name of the assembly minus the extension
The version number of the assembly Defined in the Build properties via VS
The public key value Also defined in the Build properties via VS
An optional culture identity value for localization An embedded digital signature
A combination of a hash of the assembly’s contents and the private key value
Let’s apply a strong name to our IUtils DLL…
Last Update: 3/09 Page 22
Copyright (C) 2009 by David Figge. All Rights Reserved.
Strong Names
In Project Properties / Signing Select Sign the assembly Under Choose a Strong Name Key File
Select <New…> Enter a name (IUtils) and a password of
your choice That's it. You're good. Rebuild it and copy it to the GAC
Easiest way is to drag into the GAC Copy/Paste won't work…
Last Update: 3/09 Page 23
Copyright (C) 2009 by David Figge. All Rights Reserved.
Strong Names
Note that strong names also provide some protection against tampering
Generally, it’s a good idea to strongly name every assembly intended for public consumption (even if private)
Last Update: 3/09 Page 24
Copyright (C) 2009 by David Figge. All Rights Reserved.
Extern Alias
An extern alias is used to reference to assemblies with the same fully qualified type name but different version numbers
To reference two assemblies with the same fully-qualified type names, an alias must be specified on the compiler command line, like this:
/r:GridV1=grid.dll /r:GridV2=grid20.dll
This creates the external aliases GridV1 and GridV2. To use these aliases from within a program, reference them using the extern keyword. For example:
extern alias GridV1; extern alias GridV2;
Last Update: 3/09 Page 25
Copyright (C) 2009 by David Figge. All Rights Reserved.
A Chance to Play
Last Update: 3/09
In this exercise you will build an additional DLL assembly and use it from the Address program
The DLL will have one function, called CopyToClipboard It is a utility that will copy any number of
strings to the system clipboard for pasting into other apps. In our case, it will be the entry currently displayed on the window
The DLL you create will be a shared assembly (so others can use it too )
Page 26
Copyright (C) 2009 by David Figge. All Rights Reserved.
The AddressUtils DLL
Last Update: 3/09
Create the AddressUtils DLL as an additional project in your solution
Add a reference to System.Windows.Forms So you can access the clipboard
In it, create a ClipboardUtils class, with CopyToClipboard in it public static void CopyToClipboard(params string[] p)
p is an array of strings passed by the caller Create one big long string consisting of each string in p followed by “\r\n” (each
one) Call Clipboard.Clear() to erase current clipboard contents Call Clipboard.SetText() to set the new contents to your string
From your address program Call this function copying the Name, Address, and CSZ to the clipboard
When it’s working, make the DLL a shared DLL by Signing it Placing it in the GAC
Good Luck!
Page 27
30 Minutes
Copyright (C) 2009 by David Figge. All Rights Reserved.
Advanced Language Concepts in C#
Working with Threads
Last Update: 3/09 Page 28
Copyright (C) 2009 by David Figge. All Rights Reserved.
Threads
What are threads? A thread is a “line of execution” within a process
Why are they used? To perform lengthy processes in the background
For example, loading a large image To wait for external event
For example, waiting for device to become ready To perform long-term background tasks
For example, to update a clock on the display To perform a task but keep Windows form responsive
For example, sending a file across the network When should I consider using threads in my program?
If the user interface responsiveness becomes too slow When task can easily execute in background (and is somewhat
autonomous, like printing a file) When it makes sense organizationally (for example, two tasks
that don’t interact and can be done simultaneously)
Last Update: 3/09 Page 29
Copyright (C) 2009 by David Figge. All Rights Reserved.
Thread Basics
In Windows, a thread is always contained in a process
The process “owns” the addressspace and system resources fora program
So all threads within a processhave access to the same memory,variables, functions, etc.
In a single processor machine Only one thread is actually running at a time Periodically (every 20 ms or so) the Windows
OS stops the current thread and swaps it outfor another thread that’s ready to run
This is called a Timeslice or Quantum On a dual CPU system, 2 threads are run
at a time (one for each CPU)Last Update: 3/09 Page 30
Process
Memory
Thread 1
Thread 2
FilesPorts
Copyright (C) 2009 by David Figge. All Rights Reserved.
Thread Basics
Thread States Threads are always in one of several
states. The most significant states are Ready (waiting for its turn on the CPU) Running (only one per CPU is in this state) Waiting on outside event
For example, file I/O. When this event happens, an interrupt occurs. At
that point, data is placed into its destination buffer, and the waiting thread is moved to the Ready state
Last Update: 3/09 Page 31
Copyright (C) 2009 by David Figge. All Rights Reserved.
Thread Basics
Threads and Scheduling Windows schedules threads (not processes) Threads have priority
Numbered 0-32. Above 16 is called ‘realtime’ Threads with higher priority are always run if ready
(that’s how the scheduler works) So, if my thread is higher priority than yours, yours will
stop until my thread is complete, then yours runs again Normal is about 8. Multimedia runs at about 27.
By default, the thread inherits the priority of the process creating it.
You can change your thread’s priority, but you typically leave it alone unless there’s good reason to change it
Last Update: 3/09 Page 32
Copyright (C) 2009 by David Figge. All Rights Reserved.
Threads
How do I create threads? Threads are supported in the
System.Threading namespace You create a thread by
Creating an instance of the Thread class You pass in the function to execute This just creates the object, it’s not going yet
Calling the Start() method This executes the thread It may not run until your timeslice is complete
Let’s create a simple console app to play with threading…
Last Update: 3/09 Page 33
Copyright (C) 2009 by David Figge. All Rights Reserved.
Code
Example Thread Example: Destination
static string[] destinations = { "Honolulu", "San Francisco", "Miami", "Cleveland" };static void Main(string[] args){ Destination();}static void Destination(){ Random r = new Random(); for (int x = 0; x < 25; x++) { int y = r.Next() % 4; // Choose a random
city Console.WriteLine(destinations[y]); // Display it }}
Last Update: 3/09 Page 34Type it in, then we’ll look at it. It should work…
Copyright (C) 2009 by David Figge. All Rights Reserved.
Thread Example: Destination
So this gives us a reasonable way to choose which city we should use for our next vacation Mostly, it gives us something to play
with using threads So, if one call to the destination
function is good, four must be even better, right? Let’s modify this so that we run 4
concurrent threads calculating our destination…
Last Update: 3/09 Page 35
Copyright (C) 2009 by David Figge. All Rights Reserved.
Code
Example Thread Example: Destination
static string[] destinations = { "Honolulu" ...,static void Main(string[] args){ Thread[] t = new Thread[4]; // uses System.Threading for (int x = 0; x < 4; x++) { t[x] = new Thread(Destination); t[x].Name = "Thread" + x; t[x].Start(); }}static void Destination(){ Random r = new Random(); for (int x = 0; x < 25; x++) { int y = r.Next() % 4; Console.WriteLine(Thread.CurrentThread.Name + ": " + destinations[y]); }}
Last Update: 3/09 Page 36Once again, type it in, then we’ll talk…
So, as you see, we create an array of 4 Threads.
For each one, we create the Thread object by passing the name of the
function associated with the thread. Like Main, when that function ends,
the thread terminates.
Threads have several useful properties associated with them.
Here we’re using the Name property, so we can tell each of them apart from one another.
Threads are not actually started until you call the Start function. At
that point, the thread will be placed in the queue for the next
available timeslice.
Everyone have it working? Making sense?
Copyright (C) 2009 by David Figge. All Rights Reserved.
Thread Example: Destination
So we now have 4 threads executing the same function simultaneously
Remember, to create a thread Create the object, passing the name of
the thread function to the constructor Call the .Start() method of the new
thread object It will not start until you do this
You can also pass a parameter to the thread function. Let’s see how that’s different…
Last Update: 3/09 Page 37
Copyright (C) 2009 by David Figge. All Rights Reserved.
Code
Example Thread Example: Destination
static string[] destinations = { "Honolulu" ...,static void Main(string[] args){ Thread[] t = new Thread[4]; // uses System.Threading for (int x = 0; x < 4; x++) { t[x] = new Thread(Destination); // t[x].Name = "Thread" + x; t[x].Start("Thread " + x); }}static void Destination(object name){ string MyName = (string)name; Random r = new Random(); for (int x = 0; x < 25; x++) { int y = r.Next() % 4; Console.WriteLine(MyName + ": " + destinations[y]); }}
Last Update: 3/09 Page 38Go ahead and get these changes in…
So, as you can see, we’re simply passing the name of the thread as a
parameter. Any object can be passed as the parameter (so, for
example, an array of settings)
Questionson this?
Copyright (C) 2009 by David Figge. All Rights Reserved.
Thread.Sleep()
The Sleep function is used to cause the current thread to stop executing for a specific number of milliseconds.
For example, Thread.Sleep(100) The milliseconds is approximate.
You will not return for at least 100 ms, but may be longer than that This can be useful when you’re waiting for something to
occur, and simply need to check it periodically to see if it is ready
It can also be useful when something is timed For example, counting down seconds. You can update the
time then sleep for 1 second (1000 ms) You can also use timers for these if needing it for an
extended time or often Let’s add a Sleep in our code to make sure someone
else gets a chance after we’ve printed our destination This should stop multiple prints from the same thread
Last Update: 3/09 Page 39
Copyright (C) 2009 by David Figge. All Rights Reserved.
Code
Example Thread Example: Destination
static void Destination(object name){ string MyName = (string)name; Random r = new Random(); for (int x = 0; x < 25; x++) { int y = r.Next() % 4; Console.WriteLine(MyName + ": " + destinations[y]); Thread.Sleep(5); }}
Last Update: 3/09 Page 40
Is this making sense?
Okay, so let’s put in another change and see what happens…
Copyright (C) 2009 by David Figge. All Rights Reserved.
Code
Example Thread Example: Destination
static string[] destinations = { "Honolulu" ...,static StringBuilder dest = new StringBuilder();static void Main(string[] args){ ...}static void Destination(object name){ string MyName = (string)name; Random r = new Random(); for (int x = 0; x < 25; x++) { int y = r.Next() % 4; dest.Length = 0; // Start new word for (int z = 0; z < destinations[y].Length; z++) { dest.Append(destinations[y][z]); // Copy destination to dest Thread.Sleep(1); // Pause a little } Console.WriteLine(MyName + ": " + dest); // Write destination out Thread.Sleep(5); }}
Last Update: 3/09 Page 41Put this in and see what happens…
Copyright (C) 2009 by David Figge. All Rights Reserved.
Thread Example: Destination
So what happened?!? The dest variable is an example of a shared resource
An element of the program that is (potentially) used by more than one thread at a time
What we saw was an example of Collision Where two threads USED the same resource at the same time
Generally, you want to avoid having shared resources They act as bottle-necks to the program In our case, simply making dest a local (non-static) variable would
solve that What are some real-world examples of shared resources?
I thought of: An airplane bathroom, public bathrooms in general (a limited number of resources, even if more than one), telephones, cars, televisions…
What are some computer examples of shared resources I thought of: print spoolers, shared output files, network
communications (pipes), the console output screen, databases! Anything that has only one access point and isn’t built to support
multiple threads communicating through it simultaneously
Last Update: 3/09 Page 42
Copyright (C) 2009 by David Figge. All Rights Reserved.
Avoiding Collisions
Collisions are worth avoiding They can corrupt data and can be very hard to find
How do we avoid collisions on an airplane bathroom?
A C# lock is used in the same way You ask for a resource If it’s available, you lock it, use it, unlock it If it’s unavailable, you wait until whoever has it
unlocks it (releases the lock) All objects in C# can be used in a lock
You just need to make sure everyone using the same resource is using the same lock object
Let’s see how that works…
Last Update: 3/09 Page 43
Copyright (C) 2009 by David Figge. All Rights Reserved.
Code
Example Thread Example: Destination
static string[] destinations = { "Honolulu" ...,static StringBuilder dest = new StringBuilder();static void Main(string[] args){ ...}static void Destination(object name){ string MyName = (string)name; Random r = new Random(); for (int x = 0; x < 25; x++) { int y = r.Next() % 4; lock(dest) // Apply lock via dest { dest.Length = 0; for (int z = 0; z < destinations[y].Length; z++) { dest.Append(destinations[y][z]); Thread.Sleep(1); } Console.WriteLine(MyName + ": " + dest); } // Release the lock Thread.Sleep(5); }}
Last Update: 3/09 Page 44Add this, then we’ll talk…
So only one thread can get into this code at a time. If someone else has dest locked, you must
wait for it to be released. When it’s released, a mad scramble
happens to determine who gets to be the next thread. The operating
system ensures that only one thread gets in at a time.
The lock is released at the end of the code block.
This section of code is referred to as a Critical
Section, because it’s limited to one thread at a time.
Still making sense?
Copyright (C) 2009 by David Figge. All Rights Reserved.
Deadlock
An important concern anytime you’re using shared resources/locks is deadlock (“Deadly Embrace”)
An example of deadlock situation might be the following
Thread 1 needs to update files A,B, and C Thread 2 needs to update F, and C, and B To begin
Thread 1 Starts the process by locking files A and B for update Thread 2 also starts and locks F and C
And now At this point, thread 1 tries to lock C, but thread 2 has it. Meanwhile thread 2 is waiting for file B to be released, but thread
1 has it Both threads are waiting for resources the other has, and aren’t
planning on giving them up until they’re done You now have a deadlock situation in which the program
hangs.
Last Update: 3/09 Page 45
Copyright (C) 2009 by David Figge. All Rights Reserved.
Deadlock
The two key elements to deadlock avoidance are Be aware that it happens. Look for
situations where this may occur. If you can, avoid them
If you can’t, set up a timeout situation If thread 1 is waiting for a file lock for, say,
more than 500ms Stop. Release all the locks it currently has. Wait a random (short) time Reacquire the locks
If all your threads do this, you can avoid deadlock situations
Last Update: 3/09 Page 46
Copyright (C) 2009 by David Figge. All Rights Reserved.
Background Threads
We’ve been dealing with Foreground Threads These are critical to the program and the
program does not end until these threads do The more common thread is a
Background Thread This thread runs normally, but if the program
is exited, the thread is killed In Windows, you generally want your threads
to be background threads Let’s make our threads background
threads
Last Update: 3/09 Page 47
Copyright (C) 2009 by David Figge. All Rights Reserved.
Code
Example Thread Example: Destination
static string[] destinations = { "Honolulu" ...,static void Main(string[] args){ Thread[] t = new Thread[4]; // uses System.Threading for (int x = 0; x < 4; x++) { t[x] = new Thread(Destination); t[x].IsBackground = true; // Mark threads as background t[x].Start("Thread " + x); }}
Last Update: 3/09 Page 48
Copyright (C) 2009 by David Figge. All Rights Reserved.
Background Threads
What happened? By making the “Destination” threads
background threads, we said, “If the program ends, kill these threads”
The main program ended when Main was done executing
You can suspend the current thread until another thread completes by the Join() method…
Last Update: 3/09 Page 49
Copyright (C) 2009 by David Figge. All Rights Reserved.
Code
Example Thread Example: Destination
static string[] destinations = { "Honolulu" ...,static void Main(string[] args){ Thread[] t = new Thread[4]; // uses System.Threading for (int x = 0; x < 4; x++) { t[x] = new Thread(Destination); t[x].IsBackground = true; t[x].Start("Thread " + x); } t[0].Join(); // Wait for threads to complete t[1].Join(); t[2].Join(); t[3].Join();}
Last Update: 3/09 Page 50
Copyright (C) 2009 by David Figge. All Rights Reserved.
Race Conditions
In addition to deadlocks, you also need to keep an eye out for race conditions
A race condition is where one thread assumes a second thread has completed enough of its work for the first thread to go forward
An example Thread 1’s job is to open a file In the meantime, thread 2 is processing the information
that’s going to go in the file Since this takes about 2 seconds, thread 1 should be easily
finished when this is done, so thread 2 just writes the data to the file thread 1 just opened.
But, what if thread 1 was delayed opening the file (maybe another process had it locked…)
In this case, thread 2 would try to write data to a closed file (and fail, obviously)
Last Update: 3/09 Page 51
Copyright (C) 2009 by David Figge. All Rights Reserved.
Race Conditions
You can easily avoid race conditions with two steps
Be aware that it happens. Look for situations where this may occur. If you can, avoid them.
If you can’t, simply have a flag that thread 1 sets when the file is opened.
Thread 2 then makes sure that the flag is set before continuing with the writing
If it isn’t set, either wait (sleeping is best) for it to be set or throw an exception
You can also use Events (AutoResetEvent and ManualResetEvent)
Technically, our previous problem was in our program was a race condition
Our main thread ‘assumed’ the other threads had completed and exited the program
Last Update: 3/09 Page 52
Copyright (C) 2009 by David Figge. All Rights Reserved.
Background Threads
When a C# application starts, it is given one thread
This thread is intended for use by the UI, and not for significant background processes
If you use this thread for lengthy operations, it will appear to hang the UI
If this happens, the first step is to set the form’s cursor to the hourglass
But this still causes the UI to hang, so don’t do this too long The next step would be to use a background thread to
perform the operation Ideally, the main program could continue But even if the main program has to wait, at least you could
have a cancel button to abort Printing is an obvious example However, you run into a problem…
Last Update: 3/09 Page 53
Copyright (C) 2009 by David Figge. All Rights Reserved.
The UI Problem
Say, for example, you have a print option that displays its progress to a ProgressBar control
Simple, right… Create the form and display it Start the printing thread
As the print thread progresses, have it update its progress on the progress bar control
When it’s done, it closes the “printing” form Unfortunately, it’s not quite that simple
For one thing, only the thread that created the control can access it
This is because controls aren’t designed to be accessed by multiple threads at the same time
Last Update: 3/09 Page 54
Copyright (C) 2009 by David Figge. All Rights Reserved.
The BackgroundWorker
The solution to this is a thread class specifically designed for background tasks called the BackgroundWorker
Among other things, it let’s you Specify the function that does the work Specify a function that updates
progress Specify a function to cancel the
operation Specify a function to call when the job’s
doneLast Update: 3/09 Page 55
Copyright (C) 2009 by David Figge. All Rights Reserved.
Class Exercise
Last Update: 3/09
Going back to our Address program, let’s add a “Print” button that uses the BackgroundWorker class to simulate printing a record to the printer In our case, instead of actually printing,
we’re going to wait for 5 seconds As we wait, we’ll display a progress bar If you wish, you can swap this code later
for code that actually prints But this also gave us the opportunity to look
at progress bars Page 56
Copyright (C) 2009 by David Figge. All Rights Reserved.
Printing: Step 1 – the Print Form
Create a form like this: Call in frmPrint Button: btnCancel Progress Bar: pBar
Proper OOP design says that the form should be in charge of its own controls, so add these functions to the frmPrint code…
Last Update: 3/09 Page 57
Copyright (C) 2009 by David Figge. All Rights Reserved.
Code
Example Print Form Code
BackgroundWorker WorkerThread; // The ‘printing’ threadpublic frmPrint(BackgroundWorker thread){ WorkerThread = thread; // Save so we could cancel InitializeComponent();}
public void UpdateProgress(int amt){ pBar.Value = amt; // Progress so far}
// Add this function by double-clicking the cancel buttonprivate void btnCancel_Click(object sender,RoutedEventArgs e){ WorkerThread.CancelAsync(); // Cancel printing}
Last Update: 3/09 Page 58As usual, type, then talk. It won’t work for a while…
UpdateProgress is used to update the progress bar.
That’s done simply by setting the .Value property
of the bar.
If the user presses the Cancel button, we need to signal to the BackgroundWorker thread that it
needs to abort. We do that by calling its CancelAsync method. Of course, to do that, we need access to the BackgroundWorker thread
object…
So we modify the constructor to take the
BackgroundWorker thread object, and store it into a class variable.
Is this making senseso far?
Copyright (C) 2009 by David Figge. All Rights Reserved.
Printing: Step 2 – the Print Button
Add a button to your main Address Book form called btnPrint. Double-click it to create the btnPrint_Click method
In this method we’ll Create the BackgroundWorker thread Point its delegates to functions that
Do the work (simulate printing) Update the progress bar Complete the printing
Create the frmPrint form Start the ‘printing’ process Display the frmPrint form with ShowDialog
Last Update: 3/09 Page 59
Copyright (C) 2009 by David Figge. All Rights Reserved.
Code
Example btnPrint Code
frmPrint printform = null; // New Class Variableprivate void btnPrint_Click(object sender,RoutedEventArgs e)
{ BackgroundWorker printthread = new BackgroundWorker(); printthread.DoWork += Print; printthread.ProgressChanged += PrintProgress; printthread.RunWorkerCompleted += PrintCompleted; printthread.WorkerReportsProgress = true; printthread.WorkerSupportsCancellation = true;
printform = new frmPrint(printthread); // Create form printthread.RunWorkerAsync(); // Call DoWork() // Display form, if cancelled say so if (printform.ShowDialog() == false) MessageBox.Show("Printing Aborted");}
Last Update: 3/09 Page 60Type this in. It still won’t work, but we’re closer…
So the BackgroundWorker class has several delegates that point to functions used. Here
we set up those delegates to point to our functions. Questions on that part?
Next we set some flags to show what
capabilities we’re supporting (report
progress and cancel).Next we create the frmPrint object, passing the BackgroundWorker thread (so it can cancel)
Before we display the form, we need to start the printing. This is (ultimately)
calling the .Start method on the thread.
Now we display the dialog box. When it
comes back, printing is either completed or was
cancelled.
Questions onthis code?
Copyright (C) 2009 by David Figge. All Rights Reserved.
The PrintProgress Function
The PrintProgress function’s purpose is to update the Progress Bar to the current state of the “printing” Note that this function is called in the context of
the UI thread, so it can update the control without throwing an exception
Progress default to counting from 0 to 100 (i.e. percent) We’ll call this function every 5 percent of the
way through the ‘printing’ This function will simply pass this on to the
printform’s UpdateProgress function Here’s the code…
Last Update: 3/09 Page 61
Copyright (C) 2009 by David Figge. All Rights Reserved.
Code
Example The PrintProgress Function
private void PrintProgress(object sender, ProgressChangedEventArgs e){ printform.UpdateProgress(e.ProgressPercentage);}
Last Update: 3/09 Page 62Get this typed in first, then we’ll cover it…
This is accessed through the ProgressChanged delegate of the
BackgroundWorker. When we call that delegate, we specify what progress we’ve
made, which is placed into the ProgressPercentage variable. We’ll cover
that shortly.
Copyright (C) 2009 by David Figge. All Rights Reserved.
The PrintCompleted Function
The PrintCompleted function is called in one of two circumstances Both indicate that the process is done First: print finished normally Second: printing was aborted
So our code will tell the Print form to return either DialogResult.OK or DialogResult.Cancel based on if it was cancelled or not
Here’s that code…
Last Update: 3/09 Page 63
Copyright (C) 2009 by David Figge. All Rights Reserved.
Code
Example The PrintCompleted Function
private void PrintCompleted(object sender, RunWorkerCompletedEventArgs e){ printform.DialogResult = (e.Cancelled == true ? false : true); printform.Close();}
Last Update: 3/09 Page 64Type, then talk…
This is called automatically when the Thread’s function ends. We set the
DialogResult property of the frmPrint to indicate whether we ended by
completion or cancelation. Then we close the dialog box (returning to the btnPrint
function).
Copyright (C) 2009 by David Figge. All Rights Reserved.
The Print Function
Finally we’re ready for the Print function itself
Remember its purpose is to Run for about 5 seconds Update the progress bar every 5% Cancel the process if the cancel button was
pressed The button press called AsyncCancel Which sets the CancellationPending flag of the
DoWorkEventArgs argument So we’ll check that
End when we’ve done 100% of the work Here’s the code…
Last Update: 3/09 Page 65
Copyright (C) 2009 by David Figge. All Rights Reserved.
Code
Example The Print Function
public void Print(object sender, DoWorkEventArgs e){ BackgroundWorker thread = (BackgroundWorker)sender; int currentpct = 0; do { if (thread.CancellationPending) { e.Cancel = true; return; } thread.ReportProgress(currentpct); Thread.Sleep(250); currentpct += 5; } while (currentpct <= 100);}
Last Update: 3/09 Page 66The final code. Type it in, it should work…
We start by taking sender (which is the
BackgroundWorker) and saving it so we can use it
later.
Within our loop, we first see if we’ve been asked to cancel. If so, we set the
cancel flag to true and exit the function (which calls the PrintCompleted function
automatically)
Otherwise, we report our progress (which calls
our PrintProgress function, which updates
the progress bar).
We simulate printing activity by sleeping for a
¼ second…Change our progress
status…And keep it up until
we’re done (at which point we exit, and
PrintCompleted is called automatically)
There’s a lot here. Any questions on this?
Copyright (C) 2009 by David Figge. All Rights Reserved.
Thread Summary
Threads are worth considering when Working in foreground thread is too long When task is somewhat stand-alone and can easily execute
in background It makes sense organizationally
Create a thread via the Thread class Pass the function to execute Call the Start() method to begin the thread Call the Join() function to wait for it to end
Be aware of Collisions (use locks to avoid this) Deadlock (use timeouts to avoid this) Race conditions (use flags to avoid this)
The BackgroundWorker class is convenient Allows easy creation of background tasks Supports UI updates on progress and easy cancellation
Last Update: 3/09 Page 67
Any questionson threads?
Copyright (C) 2009 by David Figge. All Rights Reserved.
Advanced Language Concepts in C#
End of Session 3
Last Update: 3/09 Page 68