two DispatchSourceTimers sharing one resource error, multithread task - ios

I have to make a simple task - singleview app with two buttons and three treads -
Start Button :
create a thread T1 - the GPS location of the device is collected repeatedly every 3.0 seconds, and the results (as a string) are handed over to T3.
create w thread T2 - the percentage usage of the device's battery is collected repeatedly every B seconds, and the results (as a string) are handed over to T3.
Data from T1 and T2 should be stored in array with limited positions (maxData).
In Thread T3, if array reaches maxData data should be send to the server (URL) via HTTP. Then clear array
STOP button - just stop all three threads
I am a total noob in iOS threading. Unfortunately, App MUST use threads even if it is not necessary. In my previous question, Rob Napier suggested to use DispatchSourceTimers.
I'm close to find a proper solution, but sth is still wrong. When i clear array and post, single data from DispatchSourceTimer is LOST. I guess, that sth is wrong with concurrentDataQueue.async(flags: .barrier), and i have to block reading of array somehow (?)
Class BackgroundThreadTimer is just wrapper for DispatchSourceTimers taken from
medium article(RepeatingTimer)
//partly Pseudocode
var counter = 0
var data = [String]()
var maxData = 5
var counterInt = 0
var concurrentDataQueue = DispatchQueue(label: "dataCleanAndPostQueue", attributes: .concurrent)
var timerLocalization = BackgroundThreadTimer(timeInterval: 2)
timerLocalization.eventHandler = {
//do sth and receive String x with actual localization
//...
concurrentDataQueue.async(flags: .barrier) {
appendAndPost(phoneInfo: String(counterInt) + ": " + x)
counterInt += 1
}
}
var timerBattery = BackgroundThreadTimer(timeInterval: 3)
timerBattery.eventHandler = {
//do sth and receive String x with actual battery level
//...
concurrentDataQueue.async(flags: .barrier) {
appendAndPost(phoneInfo: String(counterInt) + ": " + x)
counterInt += 1
}
}
func appendAndPost(phoneInfo: String) {
if data.count < maxData {
data.append(phoneInfo)
} else {
let arrayString = data.joined(separator: "; ")
DispatchQueue.global(qos: .background).async {
//post arrayString to http
print(arrayString)
}
data = [String]()
}
}
//when start pressed
timerLocalization.resume()
timerBattery.resume()
//when stop pressed
timerLocalization.suspend()
timerBattery.suspend()
Could sb help? Is my
diagnosis proper? And how to fix this.

In appendAndPost, you are appending result only if the count is less than maxData.
But let’s imagine that you called this where you already had five items in the array. In that scenario, you start the request, but you’re never doing anything with the supplied value.
I would advise appending the value regardless, and sending if the count hits the threshold:
func appendAndPost(phoneInfo: String) {
data.append(phoneInfo)
if data.count >= maxData {
// create your request and send it
...
// reset `data`
data = []
}
}

Related

DispatchQueue threads don't always set the correct results

Am trying on coding game MineSweeper, the following code is to set the numbers around landmines. For a test, I choose the minimum level 9 x 9 with 10 landmines.
For a faster performance, I tried to use more threads when setting numbers, but one day I found that it doesn't always give the correct number arrangements, I made a loop to create it 1000 times and found that 20 ~ 40 out of 1000 are wrong.
Here are several wrong results, "*" represents landmine, "0" means no landmine around
wrong mine: index 1 should be "*"
10212*100
112*21211
0011101*1
000000111
000011100
01123*200
01*2**200
023432100
01**10000
wrong num: index 0 should be "1"
0*212*100
112*21211
0011101*1
000000111
000011100
01123*200
01*2**200
023432100
01**10000
wrong num: index 73 should be "1"
1*212*100
112*21211
0011101*1
000000111
000011100
01123*200
01*2**200
023432100
00**10000
Without using DispatchQueue or set DispatchSemaphore's value to 1, it gives the correct number arrangement in 1000%1000.
1*212*100
112*21211
0011101*1
000000111
000011100
01123*200
01*2**200
023432100
01**10000
Here is the sample code:
// actually indexes created randomly every time
let minesIndexArr = [59, 74, 1, 12, 50, 56, 75, 58, 5, 25]
var defaultCellArr = [String]()
var totalCellArr = [String]()
var count = 0
for _ 1...81 {
defaultCellArr.append("0")
}
runLoop()
func runLoop() {
if count == 1000 {
return
}
totalCellArr = defaultCellArr
setNums()
}
func setNums() {
let group = DispatchGroup()
let queue = DispatchQueue(label: "com.setnums", attributes: .concurrent)
let semaphore = DispatchSemaphore(value: 10)
for i in 0..<self.totalCellArr.count {
semaphore.wait()
group.enter()
queue.async(group: group, execute: {
if self.minesIndexArr.firstIndex(of: i) != nil{
self.totalCellArr[i] = "*"
}else{
var num = 0
let neighbourIndexes = self.getNeighbourIndex(i)
for v in neighbourIndexes.values {
if self.minesIndexArr.firstIndex(of: v) != nil {
num += 1
}
}
self.totalCellArr[i] = String(num)
}
group.leave()
semaphore.signal()
})
}
group.notify(queue: DispatchQueue.main) {
printMap()
count += 1
self.runLoop()
}
}
tl;dr
You are using this non-zero semaphore to do parallel calculations, constraining the degree of concurrency to something reasonable. I would recommend concurrentPerform.
But the issue here is not how you are constraining the degree of the parallelism, but rather that you are using the same properties (shared by all of these concurrent tasks) for your calculations, which means that one iteration on one thread can be mutating these properties while they are being used/mutated by another parallel iteration on another thread.
So, I would avoid using any shared properties at all (short of the final array of boards). Use local variables only. And make sure to synchronize the updating of this final array so that you don't have two threads mutating it at the same time.
So, for example, if you wanted to create the boards in parallel, I would probably use concurrentPerform as outlined in my prior answer:
func populateBoards(count: Int, rows: Int, columns: Int, mineCount: Int, completion: #escaping ([Board]) -> Void) {
var boards: [Board] = []
let lock = NSLock()
DispatchQueue.global().async {
DispatchQueue.concurrentPerform(iterations: count) { index in
let board = Board(rows: rows, columns: columns, mineCount: mineCount)
lock.synchronize {
boards.append(board)
}
}
}
DispatchQueue.main.async {
lock.synchronize {
completion(boards)
}
}
}
Note, I'm not referencing any ivars. It is all local variables, passing the result back in a closure.
And to avoid race conditions where multiple threads might be trying to update the same array of boards, I am synchronizing my access with a NSLock. (You can use whatever synchronization mechanism you want, but locks a very performant solution, probably better than a GCD serial queue or reader-writer pattern in this particular scenario.) That synchronize method is as follows:
extension NSLocking {
func synchronize<T>(block: () throws -> T) rethrows -> T {
lock()
defer { unlock() }
return try block()
}
}
That is a nice generalized solution (handling closures that might return values, throw errors, etc.), but if that is too complicated to follow, here is a minimalistic rendition that is sufficient for our purposes here:
extension NSLocking {
func synchronize(block: () -> Void) {
lock()
block()
unlock()
}
}
Now, I confess, that I'd probably employ a different model for the board. I would define a Square enum for the individual squares of the board, and then define a Board which was an array (for rows) of arrays (for columns) for all these squares. Anyway, this in my implementation of the Board:
enum Square {
case count(Int)
case mine
}
struct Board {
let rows: Int
let columns: Int
var squares: [[Square]]
init(rows: Int, columns: Int, mineCount: Int) {
self.rows = rows
self.columns = columns
// populate board with all zeros
self.squares = (0..<rows).map { _ in
Array(repeating: Square.count(0), count: columns)
}
// now add mines
addMinesAndUpdateNearbyCounts(mineCount)
}
mutating func addMinesAndUpdateNearbyCounts(_ mineCount: Int) {
let mines = (0..<rows * columns)
.map { index in
index.quotientAndRemainder(dividingBy: columns)
}
.shuffled()
.prefix(mineCount)
for (mineRow, mineColumn) in mines {
squares[mineRow][mineColumn] = .mine
for row in mineRow-1 ... mineRow+1 where row >= 0 && row < rows {
for column in mineColumn-1 ... mineColumn+1 where column >= 0 && column < columns {
if case .count(let n) = squares[row][column] {
squares[row][column] = .count(n + 1)
}
}
}
}
}
}
extension Board: CustomStringConvertible {
var description: String {
var result = ""
for row in 0..<rows {
for column in 0..<columns {
switch squares[row][column] {
case .count(let n): result += String(n)
case .mine: result += "*"
}
}
result += "\n"
}
return result
}
}
Anyway, I would generate 1000 9×9 boards with ten mines each like so:
exercise.populateBoards(count: 1000, rows: 9, columns: 9, mineCount: 10) { boards in
for board in boards {
print(board)
print("")
}
}
But feel free to use whatever model you want. But I'd suggest encapsulating the model for the Board in its own type. It not only abstracts the details of the generation of a board from the multithreaded algorithm to create lots of boards, but it naturally avoids any unintended sharing of properties by the various threads.
Now all of this said, this is not a great example of parallelized code because the creation of a board is not nearly computationally intensive enough to justify the (admittedly very minor) overhead of running it in parallel. This is not a problem that is likely to benefit much from parallelized routines. Maybe you'd see some modest performance improvement, but not nearly as much as you might experience from something a little more computationally intensive.

multithread task, Threads in ViewController? [duplicate]

I have to make a simple task - singleview app with two buttons and three treads -
Start Button :
create a thread T1 - the GPS location of the device is collected repeatedly every 3.0 seconds, and the results (as a string) are handed over to T3.
create w thread T2 - the percentage usage of the device's battery is collected repeatedly every B seconds, and the results (as a string) are handed over to T3.
Data from T1 and T2 should be stored in array with limited positions (maxData).
In Thread T3, if array reaches maxData data should be send to the server (URL) via HTTP. Then clear array
STOP button - just stop all three threads
I am a total noob in iOS threading. Unfortunately, App MUST use threads even if it is not necessary. In my previous question, Rob Napier suggested to use DispatchSourceTimers.
I'm close to find a proper solution, but sth is still wrong. When i clear array and post, single data from DispatchSourceTimer is LOST. I guess, that sth is wrong with concurrentDataQueue.async(flags: .barrier), and i have to block reading of array somehow (?)
Class BackgroundThreadTimer is just wrapper for DispatchSourceTimers taken from
medium article(RepeatingTimer)
//partly Pseudocode
var counter = 0
var data = [String]()
var maxData = 5
var counterInt = 0
var concurrentDataQueue = DispatchQueue(label: "dataCleanAndPostQueue", attributes: .concurrent)
var timerLocalization = BackgroundThreadTimer(timeInterval: 2)
timerLocalization.eventHandler = {
//do sth and receive String x with actual localization
//...
concurrentDataQueue.async(flags: .barrier) {
appendAndPost(phoneInfo: String(counterInt) + ": " + x)
counterInt += 1
}
}
var timerBattery = BackgroundThreadTimer(timeInterval: 3)
timerBattery.eventHandler = {
//do sth and receive String x with actual battery level
//...
concurrentDataQueue.async(flags: .barrier) {
appendAndPost(phoneInfo: String(counterInt) + ": " + x)
counterInt += 1
}
}
func appendAndPost(phoneInfo: String) {
if data.count < maxData {
data.append(phoneInfo)
} else {
let arrayString = data.joined(separator: "; ")
DispatchQueue.global(qos: .background).async {
//post arrayString to http
print(arrayString)
}
data = [String]()
}
}
//when start pressed
timerLocalization.resume()
timerBattery.resume()
//when stop pressed
timerLocalization.suspend()
timerBattery.suspend()
Could sb help? Is my
diagnosis proper? And how to fix this.
In appendAndPost, you are appending result only if the count is less than maxData.
But let’s imagine that you called this where you already had five items in the array. In that scenario, you start the request, but you’re never doing anything with the supplied value.
I would advise appending the value regardless, and sending if the count hits the threshold:
func appendAndPost(phoneInfo: String) {
data.append(phoneInfo)
if data.count >= maxData {
// create your request and send it
...
// reset `data`
data = []
}
}

Corrupted memory with DispatchQueue.concurrentPerform

I see a strange behavior with the following code (which runs on Playground).
import Foundation
let count = 100
var array = [[Int]](repeating:[Int](), count:count)
DispatchQueue.concurrentPerform(iterations: count) { (i) in
array[i] = Array(i..<i+count)
}
// Evaluation
for (i,value) in array.enumerated() {
if (value.count != count) {
print(i, value.count)
}
}
The result is different each time, and sometime crashes with memory corruption. It looks like a memory reallocation (of "array") is happening while another thread is accessing the memory.
Is this a bug (of iOS) or an expected behavior? Am I missing something?
This is expected behaviour. Swift arrays are not thread safe; That is, modifying a Swift array from multiple threads concurrently will cause corruption.
I realise that you are just experimenting, but even if arrays were thread safe, this would not be a good use of concurrentPerform and would probably perform worse than a simple for loop given the threading overhead.
Once you introduce an appropriate synchronisation method to guard the array update, such as dispatching that update onto a serial dispatch queue, it will definitely perform more slowly than a simple for loop
Here is the solution. Thank you for quick responses.
import Foundation
let count = 1000
var arrays = [[Int]](repeating:[Int](), count:count)
let dispatchGroup = DispatchGroup()
let lockQueue = DispatchQueue(label: "lockQueue")
DispatchQueue.concurrentPerform(iterations: count) { (i) in
dispatchGroup.enter()
let array = Array(i..<i+count) // The actual code is very complex
lockQueue.async {
arrays[i] = array
dispatchGroup.leave()
}
}
dispatchGroup.wait()
// Evaluation
for (i,value) in arrays.enumerated() {
if (value.count != count) {
print(i, value.count)
}
}
In my case I had to generate 24k elements array with Float multiple times per second and it takes around 40ms for each on old iPhone 6.
Since each element of the array is only assigned once, I've decided to try raw array using pointers:
class UnsafeArray<T> {
let count: Int
let cArray: UnsafeMutablePointer<T>
init(_ size: Int) {
count = size
cArray = UnsafeMutablePointer<T>.allocate(capacity: size)
}
subscript(index: Int) -> T {
get { return cArray[index] }
set { cArray[index] = newValue }
}
deinit {
free(cArray)
}
}
Then I used it like this:
let result = UnsafeArray<Float>(24000)
DispatchQueue.concurrentPerform(iterations: result.count, execute: { i in
result[i] = someCalculation()
})
And it worked! Now it takes 9-16ms. Also, I have no memory leaks using this code.

Swift Performance with instance property arrays

I've come across an interesting Swift performance problem, and was looking for some suggestions, analysis on why this is happening.
I have an algorithm that required hundreds of thousands of array accesses in a loop. I find that if I reference the array as an instance property (from inside the same class instance), the performance is very poor. It seems that the array is being de-referenced at each iteration. That seems strange given that the arrays are members of the same class doing the work. Wouldn't self.x not require x to be dereferenced over and over again? The equivalent Java code doesn't have the same performance problem.
In the below example, test3 takes 0.5 seconds and test4 takes 0.15 seconds.
Do I really have to go through all my code and assign locally scoped arrays every single time I do something?
Any tips/ideas would be welcome. I have the compiler optimization set to Fast-O.
Simon
EDIT: The answer is spelled out in this article here:
https://developer.apple.com/swift/blog/?id=27
Hope it helps. Long story short, private/final for the class scoped variables will remove the need for the unwanted indirection to access the array.
class MyClass {
var array_1 = [Int64] (count: 16 , repeatedValue: 0)
var array_2 = [Int64] (count: 16 , repeatedValue: 0)
func runTest3() {
// test #3
//
let start = NSDate().timeIntervalSince1970
for i in 0 ... 10000000 {
if (array_1[ i%16 ] & array_2[ i%16 ] ) != 0 {
// whatever
}
}
let passed = NSDate().timeIntervalSince1970 - start
print("3 time passed: \(passed)")
}
func runTest4() {
// test #4
//
let start = NSDate().timeIntervalSince1970
let localArray_1 = self.array_1
let localArray_2 = self.array_2
for i in 0 ... 10000000 {
if (localArray_1[ i%16 ] & localArray_2[ i%16 ] ) != 0 {
// whatever
}
}
let passed = NSDate().timeIntervalSince1970 - start
print("4 time passed: \(passed)")
}
}
https://developer.apple.com/swift/blog/?id=27
Private/Final for the class-scoped variables removes the performance problem. Reasons in the above article. Thanks everyone for the help.

This algorithm takes more time than expected

I have this code, where i would call this "checkingfunction" function. I am not using any threading in my app, I would love to use if it benefits the performance of my app.
The "checkingfunction", takes more time than i expected. It takes more than 30 seconds to complete the execution. I cant wait that long in my app. That is not good, in middle of the game.
Somebody help me out here to rewrite the function, so that i can execute it in a faster way. Some functional programming way, if possible.
func returnCharactersFromAFourLetterString(inputString : String) -> (First : Character,Second : Character, Third : Character, Fourth : Character)
{
return (inputString[advance(inputString.startIndex, 0)],inputString[advance(inputString.startIndex, 1)],inputString[advance(inputString.startIndex, 2)],inputString[advance(inputString.startIndex, 3)])
}
func checkingWords(userEnteredWord : String)
{
var tupleFourLetters = self.returnCharactersFromAFourLetterString(userEnteredWord)
var firstLetter = String(tupleFourLetters.First)
var secondLetter = String(tupleFourLetters.Second)
var thirdLetter = String(tupleFourLetters.Third)
var fourthLetter = String(tupleFourLetters.Fourth)
var mainArrayOfWords : [String] = [] // This array contains around 0.2 million words
var userEnteredTheseWords : [String] = [] // This array contains less than 10 elements
// Check for FirstLetter
for index in 0..<array.count // Array of Letters as Strings , count = 200
{
var input = array[index]
var firstWord = "\(input)\(secondLetter)\(thirdLetter)\(fourthLetter)"
var secondWord = "\(firstLetter)\(input)\(thirdLetter)\(fourthLetter)"
var thirdWord = "\(firstLetter)\(secondLetter)\(input)\(fourthLetter)"
var fourthWord = "\(firstLetter)\(secondLetter)\(thirdLetter)\(input)"
if !contains(userEnteredTheseWords, firstWord) && !contains(userEnteredTheseWords, secondWord) && !contains(userEnteredTheseWords, thirdWord) && !contains(userEnteredTheseWords, fourthWord)
{
if contains(mainArrayOfWords, firstWord )
{
self.delegate?.wordMatchedFromDictionary(firstWord)
return
}
else if contains(mainArrayOfWords, secondWord)
{
self.delegate?.wordMatchedFromDictionary(secondWord)
return
}
else if contains(mainArrayOfWords, thirdWord)
{
self.delegate?.wordMatchedFromDictionary(thirdWord)
return
}
else if contains(mainArrayOfWords, fourthWord)
{
self.delegate?.wordMatchedFromDictionary(fourthWord)
return
}
}
if index == array.count - 1
{
self.delegate?.wordMatchedFromDictionary("NoWord")
}
}
}
Input of this function is a four letter word, Inside this function i am changing each letter by looping through that 200 letters, and checking in the mainArray that, whether any of these changed words exists in mainArray. If exists, then return me that word, otherwise just return NoWord. So totally, we can see that we are checking that contains(mainArray, word) thing around 800 times, i think this is the line which consumes more time, cause mainArray contains 0.2 million words.
Use dictionaries to look up things.
When you measure times, especially with Swift code, measure a release build, not a debug build. On the other hand, measure on the slowest device capable of running your code.

Resources