Channels ▼
RSS

C/C++

Mastering Threads on MacOS X


Creating multiple threads is a simple matter of calling pthread_create() repeatedly, as shown in Listing Two.

Listing Two: Creating multiple POSIX threads.

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

int main (int argc, const char * argv[])
{
	pthread_t *tFoo, *tBar;
	int tErr, tArgF, tArgB;
	
	// create the first pthread
	tArgF = 5120;
	tErr = pthread_create(tFoo, NULL, DoTask, &tArgF);
	
	// create the second pthread
	tArgB = 1024;
	tErr = pthread_create(tBar, NULL, DoTask, &tArgB);
	
	// 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...
	//
}

Each thread can use the same task, or they can have separate tasks. But all of them must have a unique ID. The order in which each thread ends does not follow the order of creation. Thread tFoo could finish before tBar in one run. In another run, tBar could finish before tFoo.

To let threads share a resource, use a mutex to regulate access. Create it with the library method pthread_mutex_create(). This method takes two arguments: a pointer to the mutex variable (type pthread_mutex_t), and a pointer to the mutex attributes (type pthread_mutex_attr). A NULL for a second argument means the mutex will use default attributes.

pthread_mutex_t *tLock;
int tErr;
tErr = pthread_mutex_init(tLock, NULL);

The method also returns an error state. An EAGAIN state means there are not enough resources for a mutex. An EINVAL state means some of the mutex attributes are wrong. A zero state means a valid and usable mutex.

To lock the resource, use the method pthread_mutex_lock().

tErr = pthread_mutex_lock(tLock);

To release the lock, use the method pthread_mutex_unlock().

tErr = pthread_mutex_unlock(tLock);

Both methods take a mutex as input. Both return three possible error states. An EDEADLK state means the attempt will cause a deadlock. An EINVAL state means the mutex is invalid. An EPERM state means the thread is not allowed to clear the lock. And a zero state means the lock/unlock attempt succeeded.

Listing Three shows a simple way of using a mutex construct with Pthreads.

Listing Three: Use of a mutex with Pthreads.

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

// Globals
pthread_mutex_t *gLck;
int gSum;

int main (int argc, const char * argv[])
{
	pthread_t *tFoo, *tBar;
	int tErr, tArgF, tArgB;
	
	// create the mutex construct
	tErr = pthread_mutex_init(gLck);
	
	// create the first pthread
	tArgF = 5120;
	tErr = pthread_create(tFoo, NULL, DoTask, &tArgF);
	
	// create the second pthread
	tArgB = 1024;
	tErr = pthread_create(tBar, NULL, DoTask, &tArgB);
	
	// dispose all threads
	pthread_exit(NULL);
	
	// dispose the mutex construct
	tErr = pthread_mutex_destroy(gLck);
}

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

In this code, the global gLock serves as our mutex (line 5). Before we create the first thread, we used pthread_mutex_init() to create the mutex (line 14). The routine DoTask() locks the mutex with a call to pthread_mutex_lock() (line 40). It updates the global variable gSum and clears the lock with pthread_mutex_unlock() (lines 41-42).

Finally, we dispose the mutex (line 28) with a call to pthread_mutex_destroy(). Notice we dispose of the mutex right after we dispose of the threads (line 25).

Threads with Cocoa

The POSIX thread library gives us an effective and standard, portable way to create and manage threads. But it works poorly with Cocoa objects because of the latter's memory behavior. To use a POSIX thread with Cocoa objects, we have to replace each object with an equivalent CF (Core Foundation) data type. Or we could use the NSThread class.

Figure 1 shows the base methods of the NSThread class. The class has other methods, but I focus on the ones used most often. The factory method detachNewThreadSelector:toTarget:withObject: creates an instance of NSThread. It also marks the thread for autorelease. The instance method initWithTarget:selector:object: creates an instance of NSThread as well, but this instance will need an explicit release message for disposal. Both methods take three arguments: the thread task, the target class that held the task, and the input argument for that task.


Figure 1: The NSThread class highlighting commonly used methods.

The next three methods control the thread instance. The start method starts the thread task. The cancel method ends the task, regardless of whether the task is complete. It behaves much like the POSIX method pthread_cancel(). The exit method also ends a thread task, but it posts an NSThreadWillExitNotification message on the response queue before the thread terminates. And it affects only the most current thread in the thread pool.

Three other methods report the thread's current state. The isExecuting method returns a YES when the thread task is active and running. The isFinished method returns a YES if the task has completed. The isCancelled method returns a YES if the task was terminated by a cancel or an exit message.

Finally, the isMultithreaded accessor returns a YES when there is at least one active NSThread instance. The instance, however, must be made with the factory method, not with the instance method. The thread's state has no effect on the accessor's output.

As stated earlier, the NSThread class creates threads that work well with Cocoa objects. These threads can use Cocoa objects for input and for processing. Plus, NSThread builds on top of a POSIX thread. So, it inherits some of the latter's benefits.


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