Você está na página 1de 39

Objective C Mutithreading

Asfar
Process
Every application (process )
is made up of one or more threads
starts with a single thread, which runs the
application's main function
can spawn additional threads, each of which
executes the code of a specific function.
Thread
a single path of execution through the application's code
can be used to perform different tasks simultaneously.
All threads in a single application have the same
virtual memory space (Address space )
Global variables
Open files
access rights
Child processes
Pending alarms
Signals and signal handlers
Accounting information
Each thread has its own
exception handlers, a scheduling priority, thread local storage
the thread context
machine registers, the execution stack
Memory layout for a multithreaded
process
Thread scheduling
The system itself actually manages these
threads of execution, scheduling them to run
on the available cores and preemptively
interrupting them as needed to allow other
threads to run.
Advantage of Multithreading:
can improve an applications perceived
responsiveness.
can improve an applications real-time
performance on multi-core systems.
NSThread
Threads in Objective-C are encapsulated in
NSThread objects.
Create a detached thread in your application.
Use the
detachNewThreadSelector:toTarget:withObject:
class method to spawn the new thread.
[NSThread
detachNewThreadSelector:@selector(myThread
MainMethod:) toTarget:self withObject:nil];
Create a new NSThread object and call its start
method.
detached thread
threads resources are automatically reclaimed by the
system when the thread exits.
At application exit time, detached threads can be
terminated immediately but joinable threads cannot.
joinable thread
must be joined before the process is allowed to exit.
Joinable threads preferable in cases where the thread is
doing critical work that should not be interrupted, such as
saving data to disk.
only way to to create joinable threads in ios is using OS
threads library.
NSObject threading support
All objects have the ability to spawn a new thread and use it to execute
one of their methods.
The performSelectorInBackground:withObject: method creates a new
detached thread and uses the specified method as the entry point for the
new thread:
[myObj performSelectorInBackground:@selector(doSomething)
withObject:nil];
The effect of calling this method is the same as if you called the
detachNewThreadSelector:toTarget:withObject: method of NSThread with
the current object, selector, and parameter object as parameters.
When performing a selector on another thread, the target thread must
have an active run loop.
Because the main thread starts its own run loop, you can begin issuing
calls on that thread as soon as the application calls the
applicationDidFinishLaunching: method of the application delegate.
Thread Entry Routine
1. Create an Autorelease Pool

1. Setting Up an Exception Handler


2. When writing code you want to run on a separate thread,
you have two options.
One long task to be performed with little or no interruption, and
have the thread exit when it finishes.
Put your thread into a loop and have it process requests
dynamically as they arrive. Involves setting up your threads run
loop.
A run loop is an event processing loop that you use
to schedule work and coordinate the receipt of
incoming events.
The purpose of a run loop is to keep your thread
busy when there is work to do and put your thread
to sleep when there is none.
iOS has built-in support for implementing run loops
in every thread.
The app frameworks start the run loop of your
applications main thread automatically.
you must configure the run loop and start it manually for
other threads if needed.
Timers (NSTimer)
Used to call methods on a specific object at periodic intervals.
Timers are created and scheduled with a run loop.
Once a timer is scheduled, it will fire after a specified interval.
If the timer is set to repeat, it will continue to call its target
method repeatedly each time the specified interval elapses.
Timers are not guaranteed to fire exactly at the specified
interval.
Timers fire on the thread whose run loop they are scheduled
into. In most situations, unless you specifically intend to do
otherwise, your timers will get created on the main thread
and the methods that they fire will also execute on the main
thread. If you try to do too much in a method that is called by
a timer, you will stall your user interface.
If you want to use timers as a mechanism for keeping
your user interface responsive, you need to break your
work down into smaller chunks, only doing a small
amount of work each time it fires.
NSTimer *timer = [NSTimer
timerWithTimeInterval:1.0/10.0, target:self ,
selector:@selector(myTimerMethod:), userInfo:nil
repeats:YES];
NSRunLoop *loop = [NSRunLoop mainRunLoop];
[loop addTimer:timer
forMode:NSDefaultRunLoopMode];
When you schedule the timer, the run loop retains the
timer until you stop the timer
NSObject class methods for performing a selector
on one of the applications active threads.
Methods Description

performSelectorOnMain
Thread: withObject: Performs the specified selector on the applications
waitUntilDone: main thread during that threads next run loop cycle.
performSelectorOnMain These methods give you the option of blocking the
Thread: withObject: current thread until the selector is performed.
waitUntilDone:modes:

performSelector:
onThread:withObject: Performs the specified selector on any thread for
waitUntilDone: which you have an NSThread object. These
performSelector: methods give you the option of blocking the current
onThread:withObject: thread until the selector is performed.
waitUntilDone:modes:
Perfomselector methods
convenient way of delivering messages in a
synchronized manner to a single thread.
These methods let your threads deliver messages
asynchronously with the guarantee that they will
be performed synchronously by the target
thread.
Each request to perform a selector is queued on
the target threads run loop and the requests are
then processed sequentially in the order in which
they were received.
Killing a Thread
The easiest one is to just to have the main thread (the one that runs
main()) exit, which automatically kills any other threads in the
process.
Have the thread's primary method return - The method whose
selector you have in detachNewSelector:toTarget:withObject: will
automatically kill its thread when it returns, just like main kills the
program when it returns.
Call the thread's exit method - If you're in a thread that you want
to die, you can just call NSThread's exit method. This will kill the
current thread.
NSThread doesn't provide a way to kill it when it's not the current
thread.
Design your threads to respond to a cancel or exit message.
For long-running operations, stopping work periodically and checking
to see if such a message arrived. If a message come in asking the
thread to exit, perform any cleanup and exit gracefully.
Thread Costs
Memory use and performance.
memory in Kernel - core structures needed to
manage your thread and coordinate its scheduling
programs memory space - threads stack space
and per-thread data is stored in your programs
memory space.
Most of these structures are created and
initialized when you first create the threada
process that can be relatively expensive because
of the required interactions with the kernel.
Thread Costs - Production costs.
Designing a multi-threaded application can
sometimes require fundamental changes to the
way you organize your applications data
structures.
To avoid the use of synchronization, which can
impose a tremendous performance penalty on
poorly designed applications.
Designing those data structures, and debugging
problems in threaded code, can increase the time
it takes to develop a threaded application.
Avoiding those costs can create bigger problems
at runtime.
Disadvantage of Multithreaded
Add a considerable amount of complexity to your code.
Since the threads of a process share the same memory,
need to synchronize access to the shared data within
the process.
Each thread has to coordinate its actions with other
threads to prevent it from corrupting the applications
state information.
Race condition
Use synchronization(a way to serialize access to such data
structure) to avoid it.
compiler optimizations introduce subtle bugs
Even with proper protections in place, you still have to
watch out for bugs into your code.
Use volatile variable to avoid it
Race Conditions
Every thread can access the same memory can
cause problems if youre not conscious of that
fact while programming.
When a program doesnt give the expected result
because shared data is accessed concurrently by
multiple threads, a race condition is said to exist.
Race conditions can happen when a thread
operates on the assumption that it is the sole
user of a resource that is actually shared with
other threads.
RACE CONDITION
RACE CONDITION
mutex lock
Used for avoiding race conditions
use locks to protect a critical section of your
code
a segment of code that only one thread at a time
is allowed access
A mutex lock is a mechanism used to ensure
that while a piece of code is executing, other
threads cant fire that same piece of code or
related code.
NSLock and NSRecursiveLock
They all conform to the NSLocking formal
protocol.
This protocol has two methods: lock and unlock.
Each thread 'locks' the data they're about to
modify, so that no other thread will try to modify
it or read it while it's being modified.
These classes didnt integrate well with exception
handling.
If you acquired a lock and then called something that
threw an exception, you would have problems. The
lock would never be released.
Synchronization issues
Synchronization tools are effective only when
they are used consistently by all threads in an
application.
If you create a mutex to restrict access to a
specific resource, all of your threads must
acquire the same mutex before trying to
manipulate the resource. Failure to do so
defeats the protection offered by the mutex
and is a programmer error.
@synchronized
No need to create the mutex or lock object directly.
Use any Objective-C object as a lock token:
- (void)myMethod:(id)anObj
{ @synchronized(anObj)
{ // Everything between the braces is protected by the @synchronized
directive. }
}
The object passed to the @synchronized directive is a unique identifier used to
distinguish the protected block.
If you execute the preceding method in two different threads, passing a different
object for the anObj parameter on each thread, each would take its lock and
continue processing without being blocked by the other.
If you pass the same object in both cases, however, one of the threads would
acquire the lock first and the other would block until the first thread completed
the critical section.
Used with exception. The lock is released if an exception is thrown.
Atomic Operations
do not block competing threads.
for incrementing a counter variable, this can lead
to better performance than taking a lock
OSAtomicAdd32Barrier
OSAtomicIncrement64Barrier
Example for atomic operation
@implementation Foo {
NSInteger _counter;
}

- (BOOL)veryExpensiveMethod:(id)bar {

if (_counter++ > N) {
_counter--;
return NO;
}
// Critical section
_counter--;
return YES;
}
@end
@property(nonatomic,strong) NSString * name;
atomic - the setters and getters that are
automatically created provide robust access to
instance variables in a multi- threaded
environment.
I.e., the synthesized getter and setter will have
locks used in their methods.
nonatomic - the property is just set and get the
value directly without any other consideration.
nonatomic is considerably faster than atomic.
Even if atomic is the default value, its more
common to use nonatomic properties.
Thread Safe Libraries
Knowing which libraries you use are thread-safe is critical to
successfully programming with multiple threads.
Not all libraries and frameworks are thread-safe.
Cocoa is not thread-safe.
Most of the C library is thread safe, except where specifically
noted in the documentation (eg: strtok).
The rule is fairly simple:
any library code that depends on some form of internal state
(like strtok's static pointers) is not thread-safe. Also, libraries
that have their own data structures are generally to be
considered not thread-safe unless they tell you otherwise.
All the standard C library I/O routines are thread-safe, Even
though they have their own internal state (the I/O buffers),
they protect their state through the use of thread-safe locks
like the ones mentioned above.
Update GUI only from main thread
For libraries and frameworks that you have to use that
aren't thread-safe (like Cocoa), you have to figure out
some kind of locking protocol for it.
If for example you need to use strtok, use an NSLock
around it's use in all your code, so that only one thread
accesses it at a time (or just use strrtok).
For complex libraries like Cocoa which have their own
ever-changing state, for which only one thread (the
main one) will ever be notified about, it's best to just
have one thread that does all the user-interface work
and have other threads do all the other work in the
background; this way the program always feels
responsive to the user.
NSConditionLock
Used when threads need to perform tasks in a
specific order, such as when one thread produces
data that another consumes.
While the producer is executing, the consumer
acquires the lock using a condition that is specific
to your program. (The condition itself is just an
integer value that you define.)
When the producer finishes, it unlocks the lock
and sets the lock condition to the appropriate
integer value to wake the consumer thread,
which then proceeds to process the data.
Producer - Consumer
The application contains a queue of data.
Producer thread adds data to the queue
Consumer threads extract data from the
queue.
The producer need not wait for a specific
condition(Queue full), but it must wait for the
lock to be available so it can safely add data to
the queue.
Producer
id condLock = [[NSConditionLock alloc] initWithCondition:NO_DATA];

while(true)
{
[condLock lock];
/* Add data to the queue. */
[condLock unlockWithCondition:HAS_DATA];
}
Consumer
while (true)
{
[condLock lockWhenCondition:HAS_DATA];
/* Remove data from the queue. */
[condLock unlockWithCondition:(isEmpty ? NO_DATA : HAS_DATA)];
// Process the data locally.
}
Thank You

Você também pode gostar