2

My program "ThreadsNQueues" - see below - fills text lines with "A"s and "B"s concurrently on background threads. When a text line has been filled with 10 characters it will be appended to an output text buffer (it's a NSMutableString) and this buffer should be written out to a UITextView (self.outView).

Problem: the line self.outView.text=_output; will never bee executed (tested on simulator iOS 9.2) :-( sorry i'm completely new to multithreading on iOS ...

So my question is: what is wrong with this code and how can i make it better (GCD is required)?

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

NSMutableString *_output;
NSMutableString *_line;
NSLock* _lineLock;
dispatch_queue_t _myQueue;

- (void)viewDidLoad {
    [super viewDidLoad];
    _output=[NSMutableString string];
    _lineLock=[[NSLock alloc] init];
    _line=[NSMutableString string];
    _myQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

    dispatch_async(dispatch_get_main_queue(), ^{
        do {
            dispatch_async(_myQueue, ^{
                if([_lineLock tryLock]) {
                    [self append:'A'];
                    [_lineLock unlock];
                }
            });
            dispatch_async(_myQueue, ^{
                if([_lineLock tryLock]) {
                    [self append:'B'];
                    [_lineLock unlock];
                }
            });
        } while (true);
    });
}

- (void)append:(char)c {
    [_line appendFormat:@"%c",c];
    if (_line.length==10) {
        // line is complete
        [_line appendString:@"\n"];
        [_output appendString:_line];
        NSLog(@"%@",_line);
        [_line setString:@""];
        dispatch_async(dispatch_get_main_queue(), ^{
            // update ui (never called !!!)
            self.outView.text=_output;
        });
    }
}

@end

The Xcode console log looks good:

2016-02-14 09:09:38.607 ThreadsNQueues[2807:62067] BAABAAAAAB
2016-02-14 09:09:38.611 ThreadsNQueues[2807:62067] BBAAAABAAA
2016-02-14 09:09:38.614 ThreadsNQueues[2807:62047] BABBAAABBA
2016-02-14 09:09:38.617 ThreadsNQueues[2807:62044] BBAAABAAAB
2016-02-14 09:09:38.619 ThreadsNQueues[2807:62067] BABBAABBAB
2016-02-14 09:09:38.622 ThreadsNQueues[2807:62047] ABABABABAB
2016-02-14 09:09:38.623 ThreadsNQueues[2807:62047] BBABBBABAA
2016-02-14 09:09:38.624 ThreadsNQueues[2807:62047] BBBBBBABBA
2016-02-14 09:09:38.627 ThreadsNQueues[2807:62047] AABAABAABA
...

Many thanks.

2 Answers2

2

You dispatch onto the main thread at the start of your code and then enter an infinite loop, so the main thread runloop will never run again.

If you want to use the same structure you have, dispatch onto your custom queue instead of main and then internally dispatch each async block to one of the provided priority queues.

A better solution could be to use an operation queue and have each operation add a new copy of itself onto the queue when it's done.

Wain
  • 118,658
  • 15
  • 128
  • 151
0

Update your code with the code mentioned below and reason for not updating your UI has already been mentioned in answer above:

You dispatch onto the main thread at the start of your code and then enter an infinite loop, so the main thread runloop will never run again.

Run your threads for appending string in the background and update your UI on the main thread.

- (void)viewDidLoad {
    [super viewDidLoad];
    _output=[NSMutableString string];
    _lineLock=[[NSLock alloc] init];
    _line=[NSMutableString string];


    _myQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
        //Background Thread

        do {
            dispatch_async(_myQueue, ^{
                if([_lineLock tryLock]) {
                    [self append:'A'];
                    [_lineLock unlock];
                }
            });
            dispatch_async(_myQueue, ^{
                if([_lineLock tryLock]) {
                    [self append:'B'];
                    [_lineLock unlock];
                }
            });
        } while (true);

    });

}
Shehzad Ali
  • 1,846
  • 10
  • 16
  • @Wain Thanks you for your comments! You are right, with your proposal the UI update works. But now i can see an increasing number of background threads that will not get released (memory usage rapidly increasing too). So running the code for about 20s on simulator, then pause it, makes >300 background threads. So i'm not sure if your proposal is the correct answer :-/ Another question is: is it possible to make the UI responsive so that is possible to scroll the text view. –  Feb 14 '16 at 09:32
  • 1
    Hey, i think it's the best answer :) So thanks for the answer and the code adaption. But maybe using GCD really is not the best way to solve this type of task. It might be better to create two threads writing the line "simultaneously mutual exclusive", or - as mentioned @Wain - to use NSOperation. Using GCD here leads to hundreds of temporary threads (you can reproduce this if you pause the program in Xcode). –  Feb 18 '16 at 05:08