From AS3 to C#, Part 22: Multi-Threading and Miscellany
Today’s article is the final installment in the series before we wrap things up next week. We’ll talk about C#’s built-in support for multi-threading and cover some odds and ends that were missed in the previous articles.
Table of Contents
- From AS3 to C#, Part 1: Class Basics
- From AS3 to C#, Part 2: Extending Classes and Implementing Interfaces
- From AS3 to C#, Part 3: AS3 Class Parity
- From AS3 to C#, Part 4: Abstract Classes and Functions
- From AS3 to C#, Part 5: Static Classes, Destructors, and Constructor Tricks
- From AS3 to C#, Part 6: Extension Methods and Virtual Functions
- From AS3 to C#, Part 7: Special Functions
- From AS3 to C#, Part 8: More Special Functions
- From AS3 to C#, Part 9: Even More Special Functions
- From AS3 to C#, Part 10: Alternatives to Classes
- From AS3 to C#, Part 11: Generic Classes, Interfaces, Methods, and Delegates
- From AS3 to C#, Part 12: Generics Wrapup and Annotations
- From AS3 to C#, Part 13: Where Everything Goes
- From AS3 to C#, Part 14: Built-in Types and Variables
- From AS3 to C#, Part 15: Loops, Casts, and Operators
- From AS3 to C#, Part 16: Lambdas and Delegates
- From AS3 to C#, Part 17: Conditionals, Exceptions, and Iterators
- From AS3 to C#, Part 18: Resource Allocation and Cleanup
- From AS3 to C#, Part 19: SQL-Style Queries With LINQ
- From AS3 to C#, Part 20: Preprocessor Directives
- From AS3 to C#, Part 21: Unsafe Code
- From AS3 to C#, Part 22: Multi-Threading and Miscellany
- From AS3 to C#, Part 23: Conclusion
When using multiple threads there is always an issue of thread synchronization. This is because each thread has access to the same memory and may operate on that memory at the same time. When they do, it’s really easy to cause bugs or crashes on one or many of the threads. To alleviate this, various synchronization concepts can be used. The simplest is one that C# has built in to the language: the mutex.
When multiple threads use a common mutex, only one of them can be running at a time within a particular area of code called a “critical section”. It’s good to limit the time you spend in the critical section so you’re not unnecessarily making some of the threads wait when they could be doing work. Regardless, C# provides the lock
keyword to make a critical section using some variable as the mutex. Here’s how that looks:
class Player { private int health; // Object we'll use as the mutex private Object mutex = new Object(); public void Damage(int amount) { // Code here can run regardless of who has the mutex // This code waits until the mutex is available // When available, other threads will wait on the mutex lock (mutex) { var oldHealth = health; var newHealth = oldHealth - amount; health = amount; } // Code here can run regardless of who has the mutex } }
If multiple threads were calling the Damage
function on the same Player
object, there’s no way that they could contend for the shared health
memory/variable. Each will need to wait for any other Damage
function’s lock
block to finish before applying their own damage.
While lock
is simple and straightforward, it has a bit of a performance overhead due to the use of a mutex behind the scenes. An alternative is to use volatile
field variables. These variables are marked to indicate to the compiler that it shouldn’t assume they’re only going to be used by one thread. Here’s how you use it:
class MyWorker { private volatile bool ShouldStop; public void DoWork() { while (ShouldStop == false) { // ... do work } } }
The volatile
field is useful for simple flags like ShouldStop
, but further usage is much more advanced. Unless you’re an expert, you should probably stick to lock
.
That’s about all there is for threads in the C# language, at least as far as the level of C# supported by Unity (as of 4.6). There’s no direct support at at all in AS3, so you need to work around it by using APIs like Flash’s workers. For example, you could use the Mutex class in a similar—but more verbose—way than the lock
keyword in C#.
Moving on from threading, one feature of C# left out of the discussion of the class system is the concept of partial classes. This allows you to split a class into two or more parts, perhaps even in different files. To do so, just use the partial
keyword like so:
// PlayerA.cs partial class Player { int Health; void Damage(int amount) { Health -= amount; } } // PlayerB.cs partial class Player { int Speed; void Stop() { Speed = 0; } }
Next up is the ability to call functions and specify the names of the parameters in addition to the actual parameter values to pass. To do this, simply prefix each parameter with its name and a colon like so:
// Function written as normal double CalculateHypotenuse(double sideA, double sideB) { return Math.Sqrt((sideA*sideA) + (sideB*sideB)); } // Call with named parameters CalculateHypotenuse(sideA: 2, sideB: 3);
One caveat is that once you’ve started using named parameters, the rest of the parameters need to be named. The following summarizes what’s legal and what’s not:
// Legal CalculateHypotenuse(sideA: 2, sideB: 3); CalculateHypotenuse(2, sideB: 3); CalculateHypotenuse(2, 3); // Illegal CalculateHypotenuse(sideA: 2, 3);
AS3 simply doesn’t have this feature, but you can fake it. One way is to pass an object containing the parameters rather than passing the parameters themselves. Plain Object
variables make this convenient, but accessing, building, and garbage collecting them is slow, the accesses aren’t type safe, and the necessary parameters aren’t clearly specified by looking at the parameters list. Here’s how you’d do it though:
function calculateHypotenuse(sides:Object): Number { return Math.sqrt((sides.a*sides.a) + (sides.b*sides.b)); } CalculateHypotenuse({a: 2, b: 3});
Or you could build a class just for these parameters to avoid the slow accesses and partially clarify the parameters that should be passed:
class CalculateHypotenuseParams { public var sideA:Number; public var sideB:Number; } function calculateHypotenuse(params:CalculateHypotenuseParams): Number { return Math.sqrt((params.sideA*params.sideA) + (params.sideB*params.sideB)); } var params:CalculateHypotenuseParams = new CalculateHypotenuseParams(); params.sideA = 2; params.sideB = 3; CalculateHypotenuse(params);
A final drawback to this workaround is that you can only pass parameters in this way. You can’t simply pass the literal parameters anymore. Only the C# approach where named parameters are directly supported by the language solves all of these issues.
Since the only AS3 equivalent to the C# concepts discussed today is the one we’ve just seen, we’ll skip the side-by-side comparison again this week. Stay tuned for next week where we wrap up the series and prepare to move on to all-new topics related to Unity and C#!
Spot a bug? Have a question or suggestion? Post a comment!