Book Recommendations for Economics and Investing

The housing and liquidity crisis of 2007-2008 was an eye opener for me. I was still fairly young at the time, so was lucky in that I did not have much invested in the markets. Nevertheless, it become clear to me very quickly that there was far more to the economy and markets than I assumed I knew.

As a result, I took up a new goal to absorb and understand all that I could about how the economy functions, in hopes of better serving myself and others. Since then, I've consumed about 10 or so books covering microeconomics, macroeconomics, and investing. I share with you below six books in particular that I highly recommend for starting out, should you (the reader) wish to take first steps yourself toward better understanding the complexities and realities of our modern economy.

For a philosophy to investing in the stock market:
The Only Three Questions That Count by Ken Fisher

For an introduction to the global economy (currencies, imports, exports):
Concise Guide To Macroeconomics by David Moss

For an introduction to economic development, housing, employment, and banking:
Basic Economics by Thomas Sowell

For a history of paper money, gold standards, and economic relations between U.S., Europe, and China:
Currency Wars by James Rickards

For analysis of the 2007-2008 housing and liquidity crisis*:
The Housing Boom and Bust by Thomas Sowell 
Freefall by Joseph Stiglitz

*This is an easily politicised subject, and I've included two books I've read and recommend, from two fairly different viewpoints.

Comment or email me with other suggestions that you feel would benefit new learners in economics.
3. July 2012 08:21 by SirDuck | Comments (0) | Permalink

Observation: The Absence of Doubt

Observation:

Many beliefs we hold are not from research and careful thought. We could all probably use a little more intellectual honesty with ourselves, and it starts with the right attitude. Avoid the god complex, consider in good faith all possibilities (including those you disagree with on the outset), spend time researching before concluding, study your own viewpoint as much as those that differ from yours (you'll learn something regardless). Socrates had it right: start with knowing that you know nothing.

24. July 2011 00:30 by SirDuck | Comments (0) | Permalink

F# and Continued Fractions

As an exercise in F# and a refresher on Continued Fractions, I decided to put together some functions in F# for converting scalar values into their (Simple) Continued Fraction form, and vice versa. 

My approach for converting from Continued Fraction form to scalar was to utilize F#'s List.foldBack function to accumulate the denominators starting at the deepest level and moving "up". Rather than writing a special case for the terminal (innermost) fraction, I chose to seed the (general) accumulation with Double.MaxValue, amounting to 1.0 / (aLargeValue) or approximately 0 added to the innermost fraction.

For converting from a scalar value to Continued Fraction form, I devised a recursive function (which should, I hope, receive a tail recursion optimization) that constructs the sequence starting at the "top" and moving downward, following the basic algorithm outlined at the Wikipedia page. One of the exit conditions (if this were exact) is to check for a 0 fractional value, however due to numerical error I loosened this check to 0 + 0.1% of the most recent integer term.

Code:

(* functions *)

/// <summary>Converts a number from continued fraction representation to a scalar (binary float) representation.</summary>
let cfToScalar cf = List.foldBack (fun elem acc -> float elem + (1.0 / float acc)) cf System.Double.MaxValue

/// <summary>Converts a number from scalar (binary float) representation to a continued fraction representation.</summary>
let rec scalarToCF (s:float) (maxIters:int) = 
    if maxIters = 0 then Seq.empty
    else 
        let term = floor s
        let fraction = (s - term)
        if fraction < 0.001 * term then
            Seq.singleton (int term)
        else
            let fractionInv = 1.0 / fraction
            Seq.append (Seq.singleton (int term)) (scalarToCF fractionInv (maxIters-1))
Test:
(* testing *)

open System

let piAsCFFromWolframAlpha = Array.toList [|3; 7; 15; 1; 292; 1; 1; 1; 2; 1; 3; 1; 14; 2; 1; 1; 2; 2; 2; 2; 1; 84; 2; 1; 1;|]
let piAsScalarFromDotNet = Math.PI

printfn "π (.NET Framework): %f\n" piAsScalarFromDotNet
printfn "π CF (WolframAlpha): %A\n" piAsCFFromWolframAlpha
printfn "π (cfToScalar): %f\n" (cfToScalar piAsCFFromWolframAlpha)
printfn "π (scalarToCF): %A\n" (Seq.toList (scalarToCF piAsScalarFromDotNet 10))

let rational = 17.0/24.0
let rationalAsCFFromWolframAlpha = Array.toList [|0;1;2;2;3|]
printfn "17/24: %f\n" rational
printfn "17/24 CF (WolframAlpha): %A\n" rationalAsCFFromWolframAlpha
printfn "17/24 (cfToScalar): %f\n" (cfToScalar rationalAsCFFromWolframAlpha)
printfn "17/24 (scalarToCF): %A\n" (Seq.toList (scalarToCF rational 10))

Console.WriteLine "Press any key."
let x = Console.ReadKey()
Console.Write ""

Test Output:

π (.NET Framework): 3.141593

π CF (WolframAlpha): [3; 7; 15; 1; 292; 1; 1; 1; 2; 1; 3; 1; 14; 2; 1; 1; 2; 2;
2; 2; 1; 84; 2; 1; 1]

π (cfToScalar): 3.141593

π (scalarToCF): [3; 7; 15; 1; 292; 1; 1; 1; 2; 1]

17/24: 0.708333

17/24 CF (WolframAlpha): [0; 1; 2; 2; 3]

17/24 (cfToScalar): 0.708333

17/24 (scalarToCF): [0; 1; 2; 2; 3]

Press any key.
31. May 2011 01:27 by SirDuck | Comments (0) | Permalink

js-tipos, a Strong-typing Framework for JavaScript

Now on GitHub, js-tipos, a strong-typing framework for JavaScript. If you're a C#/Java/C++ programmer seeking ways to maintain your sanity in this crazy world called JavaScript, then this framework might be just what you're looking for.

https://github.com/duckmaestro/js-tipos/

Comments, questions, criticisms and forks welcome.

7. March 2011 01:38 by SirDuck | Comments (0) | Permalink

An Ill-Defined Probability Model and the Uniqueness of Labeling

The following started out as a thought experiment about where and how probability theory connects to our perception of the physical world around us. There may be a hole or two in my reasoning, and I'm always open to hearing opinions or questions -- so feel free to comment or contact me about this post!

Review: The Canonical Probability Model

The typical probability model by definition consist of the following:

  1. A set of outcomes Ω.
  2. A set of measurable events S such that
    1. ∀ s ∈ S, s ⊆ Ω
    2. Ω, ∅ ∈ S
    3. S is closed under union and complementation.
  3. A function P: S → ℝ such that
    1. P(α) ≥ 0 ∀ α ∈ S
    2. P(Ω) = 1
    3. α, β ∈ S, α ∩ β = ∅ ⇒ P(α ∪ β) = P(α) + P(β)

I'll call this the "canonical" probability model.

Putting ourselves in the shoes of a designer of such requirements, 3(a) and 3(b) clearly seem physically motivated.

  1. We tend to relate positive numbers to the presence of something, in this case the presence of likelihood. Further, it doesn't make sense that there be anti-likelihoods, but at the same time we can fathom a nil-likelihood. Taken all-together, we require strictly non-negative probabilities (3(a)).
  2. We want to assume that something will occur when measured. We don't envision the universe suddenly disappearing (and if it did we wouldn't be around to measure it). Though there may be some intuition to assume P(Ω) = ∞ ∈ ℝ∪{∞}, why not first keep things simple and try P(Ω) = 1 which is just as intuitive.

And then we come to 3(c). Where's the explanation in physical terms for that?

In this post, we shall construct a real-world -based probability model that violates this third requirement. We will then alter our model to fit 3(c), and compare the two for insights. First though, let's get physical...

Crossing over into the Physical Realm

At some point, a probabilistic model isn't useful unless you can sample the real-world for comparison against your model. The mathematical definitions above have no direct requirement for how your real-world sampling might work (i.e. produce elements in Ω). Let's bridge the gap between the math world and the real-world.

Let's construct a real-world sampling function M: A → 2Ω where A is a set containing imaginably possible, but not directly known whether true or not, physical states. 2Ω is a power set of set Ω of possible outcomes, and each element of 2Ω represents the presence or absence of one or more outcomes possibly perceived by our sampler. It is impossible to know the precise state of the real-world -- we are always relying on samplers (whether biological or electronic or otherwise) for our physical information. Our sampler returning data in terms of Ω enables us then to compare real-world data directly against our probability model built upon Ω.

Before moving on, let's talk about set A and the rationale behind choosing 2Ω.

Regarding A, it's perhaps difficult (or philosophically tedious?) to define all of the elements of A. One can imagine each element of A as a specific arrangement of molecules for some bounded area of the universe. One such element of A might correspond to the molecular arrangement of a quarter facing heads up; or likewise a molecular arrangement corresponding to tails facing up. Regardless, elements of A represent possible or imaginable states of the physical world.

The rationale for 2Ω and not simply Ω is two-fold. Firstly, the same physical state can be perceived in different ways: is that vision yonder a tree, a cluster of green and brown shapes, or an arrangement of molecules? Perception and labels for such are not in absolute terms, but perception is all we have. Thus the best we can do, while maintaining a sort of philosophical generality, is to imagine a construction of Ω that contains many perceptions of the same physical state. Continuing with this idea, we construct our sampler M such that it returns a subset of Ω containing each possible interpretable outcome for a given element or subset of elements in A. Thus we formally define the codomain of M to be 2Ω. For an example, consider Ω = { ..., tree, green & brown cluster of form #1234, molecular arrangement of form #5678901, ... }, and for some A1 ⊆ A, M(a∈A1) = { tree, organism, green & brown cluster of form #1234, molecular arrangement of form #5678901}.

Secondly, it makes sense (if Ω is constructed for such) to have our sampler be able to return the state of two or more outcomes describing the state of the physical world. For an example, consider two quarters in our bounded sub-universe, with Ω = { Q1_Heads, Q1_Tails, Q2_Heads, Q2_Tails }, and for some A1 ⊆ A, M(a∈A1) = { Q1_Heads, Q2_Tails }.

Now that we've defined a real-world sampler and the means for comparison against a probability model, let's see how well an example holds up...

The Ill-Defined Probability Model

Let's construct a probability model from the ground up, starting with A, Ω, and M. Suppose we have a simplified roulette wheel with only two possible values: 1 (red) and 2 (black). We can say "1" is an outcome, "red" is an outcome, "2", is an outcome, and "black" is an outcome. Thus we define Ω = { 1, 2, red, black }. It should be clear that we can create a mapping M: A → 2Ω, such that some subset of A maps to { 1, red } ∈ 2Ω, and the remaining subset maps to { 2, black } ∈ 2Ω (we assume the roulette ball will always land in one of the two slots). We can now sample the real-world for outcomes, for comparison later against the rest of our probability model.

Now let's construct S within our probability model. We can attempt to build a simple version of S by pulling single elements from Ω, but we must be closed under complementation and union. Thus we end up with S = { ∅, {1}, {2}, {red}, {black}, {1, 2}, {1, red}, ..., Ω } = 2Ω.

And finally let's construct P. Let's say we know as a physical matter of fact the roulette wheel is fair, equally favoring each of the two slots. Thus P({1}) = P({2}) = c for some value c. 1 is our value for any event at all occurring, and {1}, {2} are mappable back to the only physically possible events, thus c + c = 1, so that P({1}) = P({2}) = 0.5.

We also know that "red" occurs whenever "1" does, so we can say P(red) = P(1) = 0.5. Similarly P(black) = P(2) = 0.5 for "black" and "2". We also know certain events are mutually exclusive, such as the event { 1, 2 } which has probability 0. We have then P is:

P(α) = {
 0.0 if α = ∅,
 0.5 if α = {1} or {red} or {1, red},
 0.5 if α = {2} or {black} or {2, black},
 1.0 if α = Ω,
 0.0 for all other α
}

It is clear that 2(a), 2(b), 2(c), 3(a), and 3(b) all hold. Testing 3(c) though, our model falls short.

It's true, for instance, that {1} ∩ {red} = ∅, but P({1} ∪ {red}) = 0.5 + 0.5 = 1, which implies we always land on the red or "1" slot. This contradicts our knowledge of how our roulette wheel behaves physically. Thus our model (A,Ω,M,S,P) is not valid as a canonical probability model.

A 3(c)-compliant Model

We know A corresponds directly to imaginable states in the physical world, so there is no changing A in any meaningful way. Further Ω and M are constructed on a philosophically sound basis, I believe. So let's reconsider the construction of S, our measurable events.

We, as sophisticated observers, can perceive both the difference between red and black, and the presence of two unique symbols "1" and "2".  We are able to see that perception of the red slot occurrence is inseparable from perception of the "1" slot occurrence, so let's simplify S as S' = { ∅, { 1, red }, { 2, black }, { 1, 2, red, black } }. It can be verified that S is closed under union and complementation.

Rebuilding P yields:

P'(α) = {
 0.0 if α = ∅,
 0.5 if α = { 1, red },
 0.5 if α = { 2, black },
 1.0 if α = Ω
 0.0 for all other α
}

Again, 2(a), 2(b), 2(c), 3(a), and 3(b) hold. Further, it should be clear that 3(c) now holds in this case for all α, β ∈ S', and so our revised model (A,Ω,M,S',P') is valid as a canonical probability model.

What is the Essence Here?

Comparing our non-canonical and canonical models, what is the difference? Well, essentially the canonical model requires uniqueness in the labeling of measurable events. In our initial model (A,Ω,Μ,S,P), the same fundamental, physical state is ultimately mapped to multiple l abels (elements) in S. In our revised model (A,Ω,Μ,S',P'), each unique (in a vague sense of the word) physical state is mapped to a single element in S. 

In fact any set S'' of measurable events that contains one or more elements not unique to a particular subset of physical states will lead to a non-canonical probability model.

Proof:

Let (A'',Ω'',Μ'',S'',P'') be a model such that:

  1. 1, 2(a-c), and 3(a-b) hold from the canonical probability model.
  2. A'' is a set of imaginable physical states.
  3. M'' is a sampler function A'' → 2Ω such that for each ε1 ∈ Image(M''), there is a unique subset A1 ⊆ A'' such that M''(a∈A1)=ε and M''(b∉A1) ∩ ε = ∅.
  4. For a subset A2 ⊆ A'' there exists an ε2 ∈ Image(M'') such that M''(a∈A2)=ε2 and |ε2| ≥ 2.
  5. There exists some non-empty s1 ∈ S'' such that s1 ⊂ ε2 (strict subset), and P''(s1) > 0.

Because s1 is a strict subset of ω, there exists some non-empty s2 ⊂ ε2 - s1. Because s1 and s2 are subsets of ε2, M''(a∈A2)=ε2 always indicates both s1 and s2. Further, because s1 and s2 are only found when states of A2 are sampled, P''(s1) = P''(s2). Let γ = P''(s1). From above we have γ > 0.

Let's now assume 3(c) holds. Because s2 ⊂ ε2 - s1, we have s1 ∩ s2 = ∅. Thus by 3(c), P''(s1 ∪ s2) = P''(s1) + P''(s2) = 2γ. However s1 ∪ s⊆ ε2, thus (s1 ∪ s2) is sampled by M'' whenever s1, s2 are, which implies P''(s1) = P''(s1 ∪ s2) or γ = 2γ. But γ > 0, thus we have a contradiction and 3(c) does not hold. Q.E.D.

By contrapositive, we have that 3(c) in the canonical model implies that each subset of A'' with a corresponding element in S'' has a unique such element in S''. In other words, S'' has unique labels for physical states.

We now have a explanation in a physical (or philosophical :)) context for requiring 3(c) in the first place -- it demands we (arbitrarily) choose a unique way of perceiving and labeling each class of physical outcomes!

15. August 2010 15:59 by SirDuck | Comments (0) | Permalink

Per-pixel Alpha Blending in Win32 Desktop Applications

It took a bit of research and experimenting, but I was able to correctly achieve per-pixel alpha blending in Windows desktop applications. In addition to having tested it on Windows XP SP3 and Windows 7, the code also detects failure in the alpha blend and will fall back to the chroma keying method instead (which seems to be necessary for Remote Desktop support).

Example of Per-Pixel Alpha Blending in Windows 7:

 

Preparing The Image

For this example, I've prepared an image with some text on a colorful, pixelated background.

Color

Alpha

Seeing the alpha channel, it should be apparent that the magenta regions of the color image will actually be unseen when drawn to screen. Theoretically I could have chosen any other color for those regions; however, the code below also must be able to resort to simple chroma keying if necessary, in which case magenta is often used for it's lack of occurrence "in the wild".

Though shown separately, each were combined and exported to a single BMP (Windows Bitmap) file from Photoshop CS4. Originally I hoped I could craft my image using typical Photoshop practices, however this proved unacceptable during export. I have no idea why, but Photoshop would not export transparency layers to BMP and still preserve color and alpha properly. Instead, it was flattening my layer to a white matte, and exporting a 24-bit BMP with no alpha channel.

So, in order to export correctly from Photoshop, I needed (once satisfied with the transparency look and feel) to edit in a Custom Channel (which I named "Alpha"), and remove the transparency from my primary channels' layer altogether. Once this is done, you will see the BMP option to export alpha channel, and can choose a 32-bit format along the way.

The Essential Code

Create your window instance using WS_EX_LAYERED. This instructs Windows to treat your graphics output differently. In particular, this causes Windows to hold an additional buffer for your graphics output (I believe), and causes Windows to render your graphics output with chroma keying or alpha blending enabled.

	
//////
// Win32 setup

// create the window class
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = TEXT("Splash");
wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wcex);

// create the window instance
hWnd = CreateWindowEx(
	WS_EX_LAYERED, // <---- !!!!!
	TEXT("Splash"), 
	TEXT("Splash"), 
	WS_POPUP,
	x, 
	y, 
	g_iSplashImageWidth, 
	g_iSplashImageHeight, 
	NULL, 
	NULL, 
	hInstance, 
	NULL
);

 

Next, you must ensure your pixel colors have been premultiplied against their respective alpha values. This is a requirement of the Win32 API in order to achieve the proper and intended results. In my case, I was not able to export from Photoshop a proper 32-bit BMP file with premultiplied alpha, so I perform this at load time. If you run an off-line tool or otherwise have premultiplied pixels, then it is not necessary to perform this step. To see an example of pre-multiplying, see the downloadable source code attached to this post.

With our image pixels premultiplied, we are now ready to render to the Windows display.

//////
// WNDPROC

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_CREATE:
		if(g_bUsePerPixelAlpha)
		{
			// try the per pixel method
			bool bPaintSuccess = Paint(0, 0, hWnd);
			

	// ...
}


//////
// Specialized Paint method
bool Paint(HDC hdc, PAINTSTRUCT* ps, HWND hWnd)
{
	// ...


	// setup a "back buffer" to be same pixel 
	// format as the screen DC and same dimensions
	// as source image.
	HDC hdcScreen = GetDC(NULL);
	HDC hdcBackBuffer = CreateCompatibleDC(hdcScreen);
	HBITMAP hbmBackBuffer = CreateCompatibleBitmap(hdcScreen, g_iSplashImageWidth, g_iSplashImageHeight);
	HGDIOBJ hbmOld = SelectObject(hdcBackBuffer, hbmBackBuffer);

	// copy source image to backbuffer
	SetDIBitsToDevice(
		hdcBackBuffer, 
		0,
		0,
		g_iSplashImageWidth,
		g_iSplashImageHeight,
		0,
		0,
		0,
		g_iSplashImageHeight,
		g_pSplashImagePixels,
		reinterpret_cast<BITMAPINFO*>(g_pbmiSplashImage), 
		0
	);

	// inform Windows that we have new graphics data available.
	POINT ptSrc;
	ptSrc.x = 0;
	ptSrc.y = 0;
	SIZE size;
	size.cx = g_iSplashImageWidth;
	size.cy = g_iSplashImageHeight;
	BLENDFUNCTION bf;
	bf.AlphaFormat = AC_SRC_ALPHA;
	bf.SourceConstantAlpha = 255;
	bf.BlendFlags = 0;
	bf.BlendOp = AC_SRC_OVER;
	if(!UpdateLayeredWindow(hWnd, NULL, NULL, &size, hdcBackBuffer, &ptSrc, 0, &bf, ULW_ALPHA))
	{
		SelectObject(hdcBackBuffer, hbmOld);
		DeleteDC(hdcBackBuffer);
		return false;
	}
		
	SelectObject(hdcBackBuffer, hbmOld);
	DeleteDC(hdcBackBuffer);
	return true;
}

 

Can this method fail? Yes. In particular, it seems this method is not supported under Remote Desktop connections, and infact will fail immediately but yield no specific error code from GetLastError(). The source code provided will respond to this failure and fall back to a chroma keying method that is supported (as far as I have seen) under Remote Desktop connections.

if(!bPaintSuccess)
{ // per pixel failed
	// switch to chroma key method.
	g_bUsePerPixelAlpha = false;
	SetLayeredWindowAttributes(hWnd, CHROMA_KEY, 255, LWA_COLORKEY);
}

Epilogue: UpdateLayeredWindow vs. SetLayeredWindowAttributes

Lastly, there are some important differences between UpdateLayeredWindow() and SetLayeredWindowAttributes(). Once you create your window with WS_EX_LAYERED, you have two options:

I. Do nothing else:

  1. Allows the full range of blend effects -- opaque, uniform transparency, chroma-keying, and per-pixel alpha.
  2. WM_PAINT messages are no longer fired -- you must initiate and perform the rendering of your window yourself.

 

or II. Call SetLayeredWindowAttributes():

  1. Allows opaque, uniform transparency, and chroma-keying blend effects. Per-pixel alpha is not possible.
  2. WM_PAINT messages continue to fire as normal.

 

Source Code & Acknowledgments

The full source is available here, AlphaBlending.zip (103.07 kb), and includes the photoshop file and exported BMP.

The following posts were helpful in my quest:

  1. http://www.autohotkey.com/forum/topic24628.html&sid=b09d522553d07c4ae0a834fc21239635
  2. http://www.gamedev.net/community/forums/topic.asp?topic_id=449081&whichpage=1&#2970300
  3. http://msdn.microsoft.com/en-us/library/ms997507.aspx

 

6. June 2010 04:43 by SirDuck | Comments (0) | Permalink

Real-Time Water Flow Simulation and Visualization (Video)

By request, I've posted a video (available in 720p) of the Water Simulation below:

At the start of the video, the sim has been initialized with random height values. Once the simulation begins, you can see the effects of gravity take place immediately. Downward (actually radially outward) forces are then applied to the pool.

Comments/questions welcome.

1. June 2010 03:31 by SirDuck | Comments (0) | Permalink

HTML5 Game Engine

The HTML5 game engine begins! Let her henceforth be known as "JameE".

http://code.google.com/p/jamee/

It's off to a good start, with good encapsulation thus far and asynchronous loading of images, scripts, and serialized (human-readable) objects.

Want to participate? Let me know.

2. April 2010 10:45 by SirDuck | Comments (0) | Permalink

Open-Source Software

A convincing and informative talk on the what and why of open-source software.

http://www.youtube.com/watch?v=hmZyyBVbkOQ

 

 

29. March 2010 03:57 by SirDuck | Comments (0) | Permalink

Thread Pools

So you've decided you want to multithread something? Well, the first question you should ask yourself is (after reading this article of course!) -- "should I use the thread pool?". Here are some basics on what a thread pool is and why you should (or shouldn't) use one.

Thread Pool

  1. Manages a pool of already allocated, reusable threads, available for your evil bidding.
  2. Maintains a queue of tasks, or a to-do list if you will. This list holds on to whatever tasks give the thread pool.
  3. As long as there are tasks to do, the pool keeps its threads busy doing those tasks. Once a thread finishes a task, it moves on to a new task if there is one.
  4. Minimizes the cost associated with creating/destroy threads, because threads are reused instead of created/destroyed.
  5. Minimizes system resource usage by limiting the number of threads it will ask the operating system or virtual machine for.

When to Use a Thread Pool

  1. If you're concerned about your main thread being held up for too long on less-than-important pieces of logic, then you should consider off-loading some of your algorthm to the thread pool.
  2. Thread pools are meant for tasks that will completely relatively quickly. Imagine a line of people and your task is a person -- if you are holding up the line, you are preventing everyone else from doing what they need to do too. Though this is what you wanted to avoid in the first place with regard to your main thread, if too many threads are taking too much time it begins to defeat the purpose of the thread pool, as well as rob other, possibly more important, tasks in the queue of timely completion.
  3. You have tasks that can be broken down into isolated chunks of work with no or relatively simple interdependencies.

When not to Use a Thread Pool

  1. If you're writing high-performance code and are multithreading to that end, requiring fine-grained control over your threads.
  2. If your tasks are highly interdependent and require complex signaling or cross-thread communication.
  3. If your tasks need to complete in a semi-predictable order.
  4. If you need to ensure a thread is on standby so-to-speak, ready at any moment for a particular task.

Other Things to Keep in Mind

  1. Many if not all of the possible trouble-makers when writing multithreaded code also apply to thread pools. Stay mindful.
  2. Because threads are reused from task to task, thread-specific data or state may carry over from the last task to the next task. For example the next task may find unexpected data in variables marked for thread-local storage.
  3. Don't take the attitude of, it's in a separate thread now so I don't need to care about unhandled exceptions. Wrong! The thread is still a thread. Some APIs/runtimes will raise an error for the whole process if a thread has an unhandled exception.

A Quick C#/.NET Example

ThreadPool.QueueUserWorkItem(
    new WaitCallback(
        delegate(object state)
        {
            // do something asynchronously
        }
    )
);

C# is great in this instance because we can use an anonmyous method (and the closure it creates for us) to very easily off-load a subtask to the thread pool. Very painless!

Lastly...

Not all thread pools are created equal. Check the documention on the thread pool or API you are working with to get a better idea of it's features and limitations!

17. March 2010 08:24 by SirDuck | Comments (0) | Permalink

Page List