From AS3 to C#, Part 22: Multi-Threading and Miscellany

Datetime:2016-08-23 02:54:20          Topic: C#  ActionScript           Share

Today’s article is the final installment inthe seriesbefore 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.

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’sworkers. For example, you could use theMutexclass 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 acomment!





About List