I have a question for this test senario:
- (void)testP{
dispatch_group_t group1 = dispatch_group_create();
dispatch_group_t group2 = dispatch_group_create();
NSString* test = #"1";
#synchronized (test) {
NSLog(#"%#π", [NSThread currentThread]);
dispatch_group_async(group1, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"%#π", [NSThread currentThread]);
#synchronized (test) {
dispatch_group_async(group2, dispatch_queue_create("com.test.Test", NULL) , ^{
NSLog(#"%#π", [NSThread currentThread]);
NSLog(#"ππππ");
});
}
});
}
dispatch_group_wait(group2, DISPATCH_TIME_FOREVER);
}
And here is the output:
2021-03-09 16:08:52.385549-0800 xctest[43085:14812554] <NSThread: 0x7faa8d004ba0>{number = 1, name = main}π
2021-03-09 16:08:52.385701-0800 xctest[43085:14812745] <NSThread: 0x7faa8b520770>{number = 4, name = (null)}π
2021-03-09 16:08:52.385848-0800 xctest[43085:14812747] <NSThread: 0x7faa8d4187c0>{number = 2, name = (null)}π
2021-03-09 16:08:52.385947-0800 xctest[43085:14812747] ππππ
Shouldn't the second dispatch_group_async never dispatch since the lock is held by thread #1. However I see that NSLog get printed in the console.
Calls to dispatch_group_async copy the submitted block and return immediately. In this case when the outermost dispatch_group_async returns the outermost #synchronized scope exits and the mutex is released before the submitted block is executed.
Related
sem = dispatch_semaphore_create(0);
// called by NSOperationQueue
-(void)waitFromUI
{
NSLog(#"%#", [NSThread currentThread]); //<NSThread: 0x60000007d7c0>{number = 3, name = (null)}
NSLog(#"%ld",dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER));
}
// called by UI Button click
-(void)responseFromUI:(BOOL)response
{
NSLog(#"%#", [NSThread currentThread]); //<NSThread: 0x600000060840>{number = 1, name = main}
dispatch_semaphore_signal(sem);
}
Refer to the code, the workflow is first waitFromUI in NSOperation block, then show a UI by performSelectorOnMainThread in this block, then click the button in this UI to call responseFromUI.
the function dispatch_semaphore_wait is not ended after i call the dispatch_semaphore_signal, it waits forever and block my main thread.
Anybody knows what's the matter with my code.
I am using BFTasks to perform some SpriteKit drawing in the background, but I'm not sure I'm using them correctly, as the drawing is locking up the main thread.
Each object is made up of several SKSpriteNodes, that are flattened before rendering. I'd like each one to render as soon as it's been flattened, i.e. when I call [self addChild:miniNode]; But it waits until all have been created, (locking the main thread) and then they appear all at once.
I've simplified my code below to show the chain of tasks:
- (void)drawLocalRelationships
{
[ParseQuery getLocalRelationships:_player.relationships block:^(NSArray *objects, NSError *error) {
[[[self drawRelationships:objects forMini:_player]
continueWithBlock:^id(BFTask *task) {
//this continues once they've all been drawn and rendered
return nil;
}];
}];
}
- (BFTask *)drawRelationships:(NSArray *)relationships forMini:(Mini *)mini
{
return [_miniRows drawSeriesRelationships:relationships forMini:mini];
}
The MiniRows class:
- (BFTask *)drawSeriesRelationships:(NSArray *)relationships forMini:(Mini *)mini
{
BFTask *task = [BFTask taskWithResult:nil];
for (Relationship *relationship in relationships) {
task = [task continueWithBlock:^id(BFTask *task) {
return [self drawRelationship:relationship mini:mini];
}];
}
return task;
}
- (BFTask *)drawRelationship:(Relationship *)relationship mini:(Mini *)mini
{
//code to determine 'row'
return [row addMiniTask:otherMini withRelationship:relationship];
}
The Row class:
- (BFTask *)addMiniTask:(Mini*)mini withRelationship:(Relationship *)relationship
{
//drawing code
MiniNode *miniNode = [self nodeForMini:mini size:size position:position scale:scale];
[self addChild:miniNode]; //doesn't actually render here
return [BFTask taskWithResult:nil];
}
I've tried running the addMiniTask method on a background thread, but it doesn't seem to make a difference. I wonder if I'm misunderstanding the concept of BFTasks - I figured they're automatically run on a background thread, but perhaps not?
BFTasks are NOT run on a background thread by default !
If you do:
BFTask * immediateTask = [BFTask taskWithResult: #"1"];
immediateTask completes, i.e. the completed property is YES, immediately in the current thread.
Also, if you do:
[task continueWithBlock:^id(BFTask *task) {
// some long running operation
return nil;
}];
Once task completes, the block is executed in the default executor, which executes blocks immediately in the current thread unless the call stack is too deep in which case it is offloaded to a background dispatch queue.
The current thread being the one where continueWithBlock is called.
So unless you're calling the previous code in a background thread, the long running operation will block the current thread.
However, you can offload a block to a different thread or queue using an explicit executor:
BFTask * task = [BFTask taskFromExecutor:executor withBlock:^id {
id result = ...; // long computation
return result;
}];
Choosing the right executor is critical:
executor = [BFExecutor defaultExecutor] the task's block is run on the current thread (the one where the task creation is performed) or offloaded to a background queue if the call stack is too deep. So it's hard to predict what will happen;
executor = [BFExecutor immediateExecutor] the task's block is run on the same thread as the previous task (see chaining below). But if the previous task was run by the default executor you don't really know which thread it is;
executor = [BFExecutor mainThreadExecutor] the task's block is run on the main thread. This is the one to use to update your UI after a long running operation.
executor = [BFExecutor executorWithDispatchQueue:gcd_queue] the task's block is run in the supplied gcd queue. Create one with a background queue to execute long running operations. The type of queue (serial or concurrent) will depend on the tasks to execute and their dependencies.
Depending on executor you will get different behaviour.
The advantage of BFTasks is that you can chain and synchronise tasks running in different threads. For example, to update the UI in the main thread after long running background operation, you would do:
// From the UI thread
BFTask * backgroundTask = [BFTask taskFromExecutor:backgroundExecutor withBlock:^id {
// do your long running operation here
id result = ...; // long computation here
return result;
}];
[backgroundTask continueWithExecutor:[BFExecutor mainThreadExecutor] withSuccessBlock:^id(BFTask* task) {
id result = task.result;
// do something quick with the result - we're executing in the UI thread here
return nil
}];
PFQuery findInBackgroundWithBlock method executes the block with the default executor, so if you call that method from the main thread, there is a great chance that the block will also execute in the main thread.
In your case, although I know nothing about SpriteKit, I would fetch all sprites, then update the UI:
- (void)queryRenderAllUpdateOnce {
NSThread *currentThread = [NSThread currentThread];
NSLog(#"current thread is %# ", currentThread);
// replace the first task by [query findObjectsInBackground]
[[[BFTask taskFromExecutor:[Tasks backgroundExecutor] withBlock:^id _Nonnull{
NSLog(#"[%#] - Querying model objects", [NSThread currentThread]);
return #[#"Riri", #"Fifi", #"LouLou"];
}] continueWithExecutor:[BFExecutor immediateExecutor] withBlock:^id _Nullable(BFTask * _Nonnull task) {
NSLog(#"[%#] - Fetching sprites for model objects", [NSThread currentThread]);
NSArray<NSString *> * array = task.result;
NSMutableArray * result = [[NSMutableArray alloc] init];
for (NSString * obj in array) {
// replace with sprite
id sprite = [#"Rendered " stringByAppendingString:obj];
[result addObject:sprite];
}
return result;
}] continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id _Nullable(BFTask * _Nonnull task) {
NSLog(#"[%#] - Update UI with all sprite objects: %#", [NSThread currentThread], task.result);
// TODO update the UI here.
return nil;
}];
}
But with this solution, all sprites are fetch (flattened ?) then the UI update. If you want to update the UI, every time a sprite is fetched, you could do something like this:
- (void)queryRenderUpdateMany {
NSThread *currentThread = [NSThread currentThread];
NSLog(#"current thread is %# ", currentThread);
[[[BFTask taskFromExecutor:[Tasks backgroundExecutor] withBlock:^id _Nonnull{
NSLog(#"[%#] - Querying model objects", [NSThread currentThread]);
return #[#"Riri", #"Fifi", #"LouLou"];
}] continueWithExecutor:[BFExecutor immediateExecutor] withBlock:^id _Nullable(BFTask * _Nonnull task) {
NSArray<NSString *> * array = task.result;
NSMutableArray * result = [[NSMutableArray alloc] init];
for (NSString * obj in array) {
BFTask *renderUpdate = [[BFTask taskFromExecutor:[BFExecutor immediateExecutor] withBlock:^id _Nonnull{
NSLog(#"[%#] - Fetching sprite for %#", [NSThread currentThread], obj);
return [#"Rendered " stringByAppendingString:obj];
}] continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id _Nullable(BFTask * _Nonnull task) {
NSLog(#"[%#] - Update UI with sprite %#", [NSThread currentThread], task.result);
return nil;
}];
[result addObject: renderUpdate];
}
return [BFTask taskForCompletionOfAllTasks:result];
}] continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id _Nullable(BFTask * _Nonnull task) {
NSLog(#"[%#] - Updated UI for all sprites", [NSThread currentThread]);
return nil;
}];
}
Here the middle task create a task that will complete once all renderUpdate tasks have completed.
Hope this help.
B
I'm using CoreBluetooth to connect and exchange data with a peripheral device using Bluetooth Low Energy. To connect to my peripheral I'm using the following method (for clarity, manager is an instance of the CBCentralManager class).
- (void)connectPeripheral:(CBPeripheral *)peripheral {
// Connects with the peripheral
[manager connectPeripheral:peripheral options:nil];
}
Now I wish to write the asynchronous version of this method using Grand Central Dispatch, blocks and semaphores. I would like to have a version that times out within a specific time interval. First I defined this method:
void dispatchAsyncWithCompletionAndTimeout(dispatch_queue_t queue, int64_t timeoutInNanoseconds,
dispatch_block_t block, dispatch_block_t completionBlock,
dispatch_block_t timeoutBlock) {
NSCParameterAssert(queue);
NSCParameterAssert(timeoutInNanoseconds >= 0);
NSCParameterAssert(block);
dispatch_async(queue, ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, timeoutInNanoseconds);
dispatch_async(queue, ^{
long timedOut = dispatch_semaphore_wait(semaphore, timeoutTime);
if (timedOut) {
if (timeoutBlock) {
timeoutBlock();
}
} else if (completionBlock) {
completionBlock();
}
});
block();
dispatch_semaphore_signal(semaphore);
});
}
This function basically contains three blocks: the first is the action block (e.g. connect) and the second and third one are the handler blocks which should be called whether the action code is performed within or after the timeout expiration, respectively.
What I did next is to turn my connectPeripheral: method into an asynchronous method by wrapping it inside the following method:
- (void)connectPeripheralAnsync:(CBPeripheral *)peripheral withinTimeout:(NSInteger)timeout {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatchAsyncWithCompletionAndTimeout(queue, (int64_t)timeout * NSEC_PER_SEC, ^{
[self connectPeripheral:peripheral];
}, ^{
NSLog(#"Peripheral discovered");
}, ^{
NSLog(#"Time runned out");
});
}
Unfortunately my third block never gets called, even if no peripheral could be discovered by the manager until the timeout expires. Where do I fail?
Looks like there's no problems with GCD
i get you code and remade it a little bit to check timeout stuff easy, so changing secsForTask and secsForTimeout I can check what called:
unsigned int secsForTask = 3;
unsigned int secsForTimeout = 2;
dispatch_queue_t queue = dispatch_queue_create("com.test.111", DISPATCH_QUEUE_CONCURRENT);
int64_t timeoutInNanoseconds = secsForTimeout * NSEC_PER_SEC;
dispatch_async(queue, ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, timeoutInNanoseconds);
dispatch_block_t timeoutBlock = ^{
NSLog(#"timeout");
};
dispatch_block_t completionBlock = ^{
NSLog(#"completion");
};
dispatch_block_t block = ^{
NSLog(#"block start");
sleep(secsForTask);
NSLog(#"block end");
};
dispatch_async(queue, ^{
long timedOut = dispatch_semaphore_wait(semaphore, timeoutTime);
if (timedOut) {
if (timeoutBlock) {
timeoutBlock();
}
} else if (completionBlock) {
completionBlock();
}
});
block();
dispatch_semaphore_signal(semaphore);
});
on secsForTask = 3 and secsForTimeout = 2
block start
timeout
block end
on secsForTask = 1 and secsForTimeout = 2
block start
block end
completion
probably you have to check if everything is ok with your timeout, so you don't pass in method connectPeripheralAnsync:withinTimeout: timeout in nanosecs.
-[CBCentralManager connectPeripheral: options:] method is a non-block async method.
so, this method call alway pass through next line, immediately.
in your dispatchAsyncWithCompletionAndTimeout(...) method case.
the block that contains "-[CBCentralManager connectPeripheral: options:]" executed.
and then dispatch_semaphore_signal(semaphore); executed without blocking thread.
so, you always cannot get timeout.
-[CBCentralManager connectPeripheral: options:] call will response with
-[CBCentralDelegate centralManager: didConnect:] when connected successfully.
so, you should approach differently.
dispatch_semaphore_signal(semaphore); should call on -[CBCentralDelegate centralManager: didConnect:].
If libsqlite is not thread safe a code like that
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
__block NSArray *__albumsCollection = albumCollections;
dispatch_apply(count, queue, ^(size_t i)
{
MPMediaItem *albumObj = [[__albumsCollection objectAtIndex:i] representativeItem];
///// making some sqlite queries
});
would raise a BAD_EXEC.
So how to make this code thread safe?
My solution was using the main queue
dispatch_apply(count, dispatch_get_main_queue(), ^(size_t i)
{
/// my sqllite queries
});
but I'm not satisfied with that. How to make it better?
Instead of using dispatch_get_main_queue() to get the main queue you might want to create a separate private dispatch queue on a non-main thread like so:
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", DISPATCH_QUEUE_SERIAL); // or NULL as last parameter if prior to OS X 10.7/iOS 5.0
dispatch_apply(count, queue, ^(size_t i) {
/// your SQLite queries
});
Alternatively you could use a FMDatabaseQueue from Gus Mueller's (#ccgus) brilliant FMDB SQLite wrapper framework (which is what I'd do):
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
[queue inDatabase:^(FMDatabase *db) {
// Your SQLite queries:
[db executeQuery:#"...", ...];
...
}];
β¦which will send your query block to a serial dispatch queue, wrapping its execution synchronously.
Not convinced yet?
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Your SQLite queries:
[db executeQuery:#"...", ...];
...
}];
How about now?
Also, custom-defined block-based SQLite functions.
dispatch_queue_t q=dispatch_queue_create("name", NULL);
dispatch_sync(q, ^{
//your code is here
});
dispatch_async(dispatch_get_main_queue(), ^(void) {
//as soon as above block is completed, this block executed and you will be notified that
//work is completed
});
I have the following method
+ (NSString*)getMeMyString
{
NSString *result;
dispatch_async(dispatch_get_main_queue(), ^{
result = [ClassNotThreadSafe getString];
});
return result;
}
How can i make the block to do it's job synchronously, so that it doesn't return the result before it was retreived?
You are calling dispatch_async which dispatches your block asynchronously. Try using dispatch_sync or dispatch_main if your goal is to block the main thread.
+ (NSString*)getMeMyString
{
__block NSString *result;
dispatch_sync(dispatch_get_main_queue(), ^{
result = [ClassNotThreadSafe getString];
});
return result;
}
Grand Central Dispatch Reference
Use dispatch_sync instead of dispatch_async - then the current thread will be blocked until the block has finished executing on the main thread.
Since it seems like you want to perform a method on a different thread and get a return value, why don't you use an NSInvocation?
SEL theSelector;
NSMethodSignature *aSignature;
NSInvocation *anInvocation;
theSelector = #selector(getString);
aSignature = [ClassNotThreadSafe instanceMethodSignatureForSelector:theSelector];
anInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
[anInvocation setSelector:theSelector];
NSString *result;
[anInvocation performSelectorOnMainThread:#selector(invoke) withObject:nil waitUntilDone:YES];
[anInvocation getReturnValue:result];