HI,
I’m just learning Swift and was looking at Grand Central Dispatch (GCD) for some CPU intensive tasks. Here are (I believe) all the relevant bits of code I’m puzzled by:
The problem I’m having is that unless I uncomment the line just above work_q.async() in permute(), when check_squares() starts, ary has zero elements when I expect it to have 1,000 elements. Right after I enqueue check_squares() to GCD on the global async queue, I do perm_q = [[Int]]() which empties array perm_q to be ready to collect the next 1,000 elements.
I’m guessing there is a race condition between starting check_squares() and emptying perm_q, and the emptying occurs before check_squares() starts, but I’m puzzled as to why this race occurs. I understood that the call to check_squares() would make a copy of perm_q, and I thought this copy would happen with the call to check_squares().
One explanation I thought of is that the copy of perm_q into check_squares()’s param ary doesn’t happen until GCD starts to execute check_squres(). By the time this happens, perm_q has been emptied. Is that when the copy of perm_q into ary happens and not when check_squares() is enqueued? Making the local copy of global var perm_q into var p local to permute() and passing p to check_squares() during the enqueue makes local var p stick around since the reference from check_squares() in the queue keeps array p from disappearing even after permute() exits. Does this sound right?
Other than making the local copy of perm_q into p, is there a preferred method of handing this?
Thanks, Eric
I’m just learning Swift and was looking at Grand Central Dispatch (GCD) for some CPU intensive tasks. Here are (I believe) all the relevant bits of code I’m puzzled by:
Code Block // global declarations var digits = [Int]() // where the square's digits reside var perm_q = [[Int]]() // where we accumulate permutations to check later Let perm_q_max = 1000 // how many permutations we queue before checking them var enq_count = 0 // how many times we've called check_squares var N = 3 // size of N x N square let work_q = DispatchQueue.global() // returns a global, concurrent dispatch queue let work_g = DispatchGroup() // returns a group we put all our work into // func which is enqueued onto a GCD queue func check_squares( cnt: Int, perm_ary: [[Int]]) { ... } // func which enqueues check_squares() onto global GCD queue func permute( k: Int, ary: inout [Int]) { if k == 1 { perm_q.append( ary) // queue up this permutation for later magic checking // if we've queued up enough permutations, then dispatch check_squares() if ( perm_q.count >= perm_q_max) { enq_count += 1 // --> let p: [[Int]] = perm_q // make a local copy work_q.async( group: work_g) { // assign work all to one group check_squares( cnt: enq_count, // check to see if any square is magic perm_ary: p) } perm_q = [[Int]]() // clear out previous permutations } } else { ... } } // main // Create a dispatch queue onto which we'll put the square-checking tasks. digits = Array( 1 ... ( N * N)) // fill digits with digits 1...N^2 permute( k: digits.count, ary: &digits) // creates permutations and checks for magic squares
The problem I’m having is that unless I uncomment the line just above work_q.async() in permute(), when check_squares() starts, ary has zero elements when I expect it to have 1,000 elements. Right after I enqueue check_squares() to GCD on the global async queue, I do perm_q = [[Int]]() which empties array perm_q to be ready to collect the next 1,000 elements.
I’m guessing there is a race condition between starting check_squares() and emptying perm_q, and the emptying occurs before check_squares() starts, but I’m puzzled as to why this race occurs. I understood that the call to check_squares() would make a copy of perm_q, and I thought this copy would happen with the call to check_squares().
One explanation I thought of is that the copy of perm_q into check_squares()’s param ary doesn’t happen until GCD starts to execute check_squres(). By the time this happens, perm_q has been emptied. Is that when the copy of perm_q into ary happens and not when check_squares() is enqueued? Making the local copy of global var perm_q into var p local to permute() and passing p to check_squares() during the enqueue makes local var p stick around since the reference from check_squares() in the queue keeps array p from disappearing even after permute() exits. Does this sound right?
Other than making the local copy of perm_q into p, is there a preferred method of handing this?
Thanks, Eric