Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 检查同步锁定标志和退出功能的速记?_Multithreading_Concurrency_Swift3_Grand Central Dispatch - Fatal编程技术网

Multithreading 检查同步锁定标志和退出功能的速记?

Multithreading 检查同步锁定标志和退出功能的速记?,multithreading,concurrency,swift3,grand-central-dispatch,Multithreading,Concurrency,Swift3,Grand Central Dispatch,我试图阻止我的一个函数多次运行,即使是从几个不同的线程调用,但它看起来相当笨拙 以下是我正在做的: func doSomethingOnceAtATime() { var shouldExit = false DispatchQueue(label: "abcxyz123").sync { guard !inProgress else { shouldExit = true; return } inProgress = true }

我试图阻止我的一个函数多次运行,即使是从几个不同的线程调用,但它看起来相当笨拙

以下是我正在做的:

func doSomethingOnceAtATime() {
    var shouldExit = false
    DispatchQueue(label: "abcxyz123").sync {
        guard !inProgress else { shouldExit = true; return }
        inProgress = true
    }
    guard !shouldExit else { return }

    // Do something
}

我必须这样做,因为
sync
块中的
return
只退出该闭包,而不退出它所在的函数。有没有更快捷或优雅的方法来实现这一点?

第一个问题是以下代码根本不提供同步:

DispatchQueue(label: "abcxyz123").sync { ... }
这将在每次调用时实例化一个新队列。相反,您应该有一个单一串行队列的属性,然后在所有
sync
调用中使用该队列:

private let synchronizationQueue = DispatchQueue(label: "abcxyz123")

func someMethodThatNeedsSynchronizedAccess() {
    synchronizationQueue.sync { ... }
}

谈到你关于不喜欢在块之前创建局部变量的问题,我赞同这一点。至少,我倾向于将此模式从
doSomethingOnceAtATime
中拉出,并隔离同步逻辑:

class TaskState {
    private var inProgress = false
    private let queue = DispatchQueue(label: "...")

    func attemptSetInProgress() -> Bool {
        var succeed = false
        queue.sync {
            if !inProgress {
                succeed = true
                inProgress = true
            }
        }
        return succeed
    }

    func unsetInProgress() {
        queue.sync {
            inProgress = false
        }
    }
}
然后,您的
dosomethingoncatatime
变得更加直观:

let state = TaskState()

func doSomethingOnceAtATime() {
    if !state.attemptSetInProgress() { return }

    // Do something

    state.unsetInProgress()
}

但这仍然有局部变量(尽管无可否认,封装在更符合逻辑的层次上)。即使这让您感到困扰,我们也可以利用
sync
方法
rethrows
这一事实来消除这一问题。所以我们可以做一些类似的事情:

class TaskState {

    enum TaskStateError: Error {
        case alreadyInProgress
    }

    enum State {
        case inProgress
        case notInProgress
    }

    private var state = State.notInProgress
    private let syncQueue = DispatchQueue(label: "sync")

    /// Try changing task status, if we can.
    ///
    /// - Note: Throw error if state already "in progress" and trying to change it to "in progress" again.

    func change(to newState: State) throws {
        try syncQueue.sync {
            if state == .inProgress && newState == .inProgress {
                throw TaskStateError.alreadyInProgress
            } else {
                state = newState
            }
        }
    }

}
注意,上面还概括了“状态”(如果您移动到两个以上的状态)

然后你可以做:

let state = TaskState()

func doSomethingOnceAtATime() {
    do { try state.change(to: .inProgress) } catch { return }

    // Do something

    try? state.change(to: .notInProgress)
}
我必须承认,虽然这消除了局部变量,但我个人认为它并不比先前的模式好。但这取决于你