Channels ▼
RSS

C/C++

Mastering Threads on MacOS X


Thanks to its BSD lineage, MacOS X has a preemptive thread mechanism that is robust and scalable. In this article, I provide an introduction to the two key thread solutions on MacOS X going forward. I'll begin with a look at POSIX threads, then focus on the Cocoa class NSThread. I'll demonstrate how to use these solutions to create and manage a thread task. And, of course, I'll compare these approaches, and review some key guidelines for designing a thread-safe task. I'm assuming a working knowledge of ANSI C and Objective-C.

Challenges of Threading

Memory management is more difficult with threads. The main application process gets its allocated memory from the host system. It can even ask for more memory to cover its growing needs. A thread, however, gets its memory share from the main process itself. This means its stack frame is smaller than the process' frame. The thread must dispose of any objects created by its tasks. It must avoid data structures too large to fit in its stack frame. It must know how to react to low-memory signals and to VM paging.

Some threads could take advantage of garbage collection. But the collection service adds extra overhead to the thread. And it can slightly degrade thread performance.

Sometimes, threads use the same resource, which leads to coordination problems. One thread might change the data in a resource while others are trying to read it. If both actions happen at the same time, the result is data corruption.

A thread might dispose of a shared resource, but not inform its fellow threads. When the other threads try to use the resource, they will fail and crash with an EXC_BAD_ACCESS error. A thread might lock a resource for its use, but fail to clear the lock afterwards. This blocks other threads from using the resource, causing a deadlock.

Finally, a thread might fail to terminate. Instead, it stays in the background, hogging resources and processor cycles as it runs. The result is a runaway thread. It can be terminated only with a forced quit or with a hard system restart.

Thread Solutions on OS X

MacOS X offers four ways to create a threaded task. One way is with a POSIX thread (Pthread) library. Another is the Cocoa class NSThread. Both solutions create threads that are preemptively scheduled and scalable to multiple processor cores.

Threads can also be created using the Carbon library Multiprocessor Services. This library has its roots in the Macintosh clone years. It was designed to support the DayStar clones, which sport multiple PowerPC processors. It too creates preemptive and scalable threads. But these threads cannot interact with view widgets.

Finally, threads can be created with the Carbon Thread Manager. This, however, creates threads that are scheduled cooperatively and are not scalable. Also, the last two options are deprecated on MacOS X 10.6 (Snow Leopard), and are likely obsolete on 10.7 (Lion) and later. So, I will focus exclusively on the first two approaches.

Threads with POSIX

The POSIX thread library provides methods and data types for creating and managing a pre-emptive thread. It is a standardized library, its behavior defined by the IEEE document 1003-1c-1995. It is platform-agnostic, and available on all POSIX-compliant systems such as Linux, BSD, and MacOS X. The library is also available on the Windows platform through third-party sources.

There are about 100 methods and data types in the current library. All their names have the prefix pthread_. Many methods fall in one of three categories:

  • Methods for managing a threaded task. Some create and start a thread, then dispose of it afterwards. Some collect the threads into a pool. Others alter the priority levels of each thread.
  • Methods and data-types for managing mutex constructs. Mutexes are locks that enable threads to share resources by allowing only one thread to access a resource at a time. (This is referred to as mutual exclusion, a term from which the word mutex is derived.)
  • Methods for handling interactaction among threads and with the main process.

Working with POSIX Threads

To compile the Xcode project against the Pthreads library, first add the header pthreads.h into the appropriate source file using the #import directive.

#import <pthreads.h>

The #include directive can do the same task, but #import prevents the header from being added twice.

To create a thread, use the library function pthread_create().

pthread_t *tID;
int tErr;
tErr = pthread_create( tID , NULL, doFoo, NULL);

This function takes four input arguments: the thread ID, attributes, a pointer to the task routine, and a pointer to the task argument. The ID is cast as type pthread_t and passed as a pointer. Passing a NULL for attributes sets up the thread with default values. The task argument may be a primitive or a data structure. It may be a NULL for task routines that do not need input. The task routine, however, must not return an output result.

The pthread_create() function returns three possible error states. An EAGAIN state means there are not enough resources to create a thread. It can also mean the maximum number of active threads has been reached. An EINVAL state means the specified attributes are incorrect. A value of zero means an active and running thread.

To terminate a thread, it is sufficient to let the routine return or exit. This will kill the thread and this approach is what Apple recommends. However, if you wan to force the process, you can use pthread_exit(). It takes one argument, a generic pointer to a status variable, which points to the equivalent of the function's return value (for any threads waiting on the killed thread to return a value). Often that value is NULL:

pthread_exit(NULL);

Another way to terminate a thread is with the routine pthread_cancel(). It takes one argument, a pointer to the thread's unique ID.

pthread_t *tID;
pthread_cancel(tID);

The primary difference between the two methods is that pthread_cancel() can kill a designated thread, whereas pthread_exit() kills only the current thread.

Listing One implements a simple POSIX thread. The routine DoTask is cast as generic pointer (line 19).

Listing One: Implementing a POSIX thread

#import <pthread.h>
#import <stdlib.h>

int main (int argc, const char * argv[]) 
{
	pthread_t *tID;
	int tErr, tArg;
	
	// prepare the test argument
	tArg = 5120;
	
	// create a pthread
	tErr = pthread_create(tID, NULL, DoTask, &tArg);
	
	// dispose all threads
	pthread_exit(NULL);
}

void *DoTask(int *anArg)
{
	int tArg;
	
	// retrieve the thread argument
	tArg = *anArg;
	printf("DoTask:input:argument:%d\n", tArg);
	
	//
	// RUN THE REST OF THE TASK...
	//
}

DoTask takes a single input argument anArg, which is a pointer to an integer. The routine dereferences the argument and retrieves the integer value (line 24). It then outputs the value with printf() and proceeds with the rest of the task code (not shown).


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video