【问题标题】:Implementation of swift's synchronized not workingswift的同步不工作的实现
【发布时间】:2018-08-28 04:43:21
【问题描述】:

我是 swift 新手,我正在尝试多线程,这个概念在 swift 中似乎并不出名。基于 java 的 synchronizedimplementation 的 this 示例,我尝试根据 another SO 帖子上为 swift 提供的示例对 swift 执行相同的操作。这是我的实现:

import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

public class TestThread {
    var name : String;
    var theDemo : TheDemo;
    init(_ name : String, _ theDemo : TheDemo) {
        self.theDemo = theDemo;
        self.name = name;
        run()
    }

    public func run() {
        DispatchQueue.global(qos: .background).async {
            DispatchQueue.main.async {
                self.theDemo.testSynced(self.name)
            }
        }
    }
}

public class TheDemo {
    private func synced(_ lock : Any, _ name : String, _ closure : (_ name : String) -> ()){
        objc_sync_enter(lock)
        defer { objc_sync_exit(lock) }
        closure(name)

    }
    public func testSynced(_ name : String){
        synced(self, name, test)
    }
    public func test(_ name : String) {
        for i in 0..<4 {
            let str = "\(name) :: \(i)"
            let theDeadline = DispatchTime.now() + .seconds(i/2)
            DispatchQueue.main.asyncAfter(deadline: theDeadline, execute: {
                print(str)
            })
        }
    }

}

var theDemo = TheDemo()
TestThread("THREAD 1", theDemo)
TestThread("THREAD 2", theDemo)
TestThread("THREAD 3", theDemo)
TestThread("THREAD 4", theDemo)

当我在操场上运行上面的代码时,我得到的结果如下所示

THREAD 1 :: 0
THREAD 1 :: 1
THREAD 2 :: 0
THREAD 2 :: 1
THREAD 3 :: 0
THREAD 3 :: 1
THREAD 4 :: 0
THREAD 4 :: 1
THREAD 1 :: 2
THREAD 1 :: 3
THREAD 2 :: 2
THREAD 2 :: 3
THREAD 3 :: 2
THREAD 3 :: 3
THREAD 4 :: 2
THREAD 4 :: 3

但我期待的结果如下所示。

THREAD 1 :: 0
THREAD 1 :: 1
THREAD 1 :: 2
THREAD 1 :: 3
THREAD 2 :: 0
THREAD 2 :: 1
THREAD 2 :: 2
THREAD 2 :: 3
THREAD 3 :: 0
THREAD 3 :: 1
THREAD 3 :: 2
THREAD 3 :: 3
THREAD 4 :: 0
THREAD 4 :: 1
THREAD 4 :: 2
THREAD 4 :: 3

我想了解我做错了什么。考虑到同步和快速线程的概念,我是否也将期望设定在正确的道路上。我很感激帮助。谢谢。

编辑

由于我认为我被误解了,所以我想多解释一下我想做的事情。我基本上想要实现的与我在代码中写的有点不同。这是一个简化版本。如果synchronized 在一个简单的示例中工作,我想测试它,然后我想在另一个项目中使用它。我正在将一个为 android 编写的项目翻译为 ios。在 java 多线程中,我使用 synchronized 以便一次只有一个线程可以使用该函数。假设有多个线程可以处理的变量或函数。当我把 synchronized 放在函数名或变量名之前,线程就会有锁。现在在我的项目中,我有几个线程,它们来来去去,基于一些传感器输入——在​​它们死之前,它们使用一个公共共享函数。他们随时可能来。出于几个原因,我们设置了公共函数synchronized,这样就没有两个线程同时进入该函数。因此,当我翻译代码时,我迅速寻找类似的内容,并找到了与该问题相关的链接之一。我试图使用它——它对我不起作用。犯错是人之常情——我可能在某个地方犯了错误。但是我已经根据我的时间表花时间阅读了。 (我部分说多线程在swift中并不是很有名,因为我读过一些地方,例如this

【问题讨论】:

  • 请搜索“异步”。这已经在这里解释了很多很多次
  • 我的问题是关于关键字同步和预期的输出。我根据我在 stackoverflow 中找到的建议实现了,结果与 java 的不同。请不要对我投反对票 - 我宁愿删除这个问题。谢谢。
  • “我宁愿删除这个问题”然后删除它。
  • 我尝试添加更多描述。感谢您的回答。
  • “出于多种原因,我们将通用函数设置为同步,以便没有两个线程同时进入该函数。”美好的。所以看看我的回答:在 Cocoa 和 GCD 中,我们使用串行队列来完成同样的事情。串行队列是一种锁定形式——一种更好的形式。

标签: swift multithreading concurrency


【解决方案1】:

首先,切勿在操场上测试这些东西,因为它的输出无法正确模拟现实。在实际应用项目中进行测试。

第二,摆脱这一切:

    objc_sync_enter(lock)
    defer { objc_sync_exit(lock) }
    closure(name)

纯粹使用 GCD 做任何你想做的事。

我不清楚你想要做什么; objc_sync_enterasyncAfter 的混乱对我来说太神秘了。然而,这些东西一点也不神秘,而且有很好的记录和解释(与您的说法相反)。

从您想要的输出来看,您似乎想要将“线程 1”操作排队并让它在“线程 2”操作开始之前从头到尾运行。

  • 保证这一点的一种方法是将这些操作排队到串行队列,因为这意味着排队的操作在其前面的队列为空之前无法启动。这不会在您的代码中发生,因为您的队列是一个 并发 队列,这意味着操作同时运行(即它们的步骤可以相互交错)。
  • 另一种方法是使用 sync 而不是 async 调用“线程 1”操作,因为这意味着您的代码会等待(阻塞)直到操作完成并返回。

这是第一种方式的示例,我认为这是更好的方式:

let queue = DispatchQueue(label:"myqueue") // serial queue
func go() {
    for i in 0..<4 {
        let name = "Thread \(i)"
        queue.async {
            for i in 0..<4 {
                let str = "\(name) :: \(i)"
                print(str)
            }
        }
    }
}

调用go()时的输出:

Thread 0 :: 0
Thread 0 :: 1
Thread 0 :: 2
Thread 0 :: 3
Thread 1 :: 0
Thread 1 :: 1
Thread 1 :: 2
Thread 1 :: 3
Thread 2 :: 0
Thread 2 :: 1
Thread 2 :: 2
Thread 2 :: 3
Thread 3 :: 0
Thread 3 :: 1
Thread 3 :: 2
Thread 3 :: 3

这看起来很像你说的你想要的。

【讨论】:

    【解决方案2】:

    问题

    TheDemo

    let theDeadline = DispatchTime.now() + .seconds(i/2)
    DispatchQueue.main.asyncAfter(deadline: theDeadline, execute: {
        self.theDemo.testSynced(self.name)
    


    解决方案

    TestThread:

    DispatchQueue.global(qos: .background).async {
            let theDeadline = DispatchTime.now() + .seconds(1)
            DispatchQueue.main.asyncAfter(deadline: theDeadline, execute: {
    

    TheDemo

    for i in 0..<4 {
        let str = "\(name) :: \(i)"
        print(str)
    }
    


    说明

    所以。每个异步任务都完成得如此之快,以至于它们从现在开始被推迟到同一时间。当您将所有内容同步到同一时间时,您将深入了解 DispatchQueue 的主线程如何处理这种情况。

    //Starts at some exact time
    
    TestThread("THREAD 1", theDemo)
    TestThread("THREAD 2", theDemo)
    TestThread("THREAD 3", theDemo)
    TestThread("THREAD 4", theDemo)
    
    //Finishes at the same exact time.  Too fast!
    //
    

    在我的解决方案中,打印在单一时间上下文中(您创建所有自定义线程的操场主队列)。所以它会按您的预期打印。您必须使用更精细的单位(没有 [纳秒]),或者像我展示的那样设计它。

    【讨论】:

    • 嗯...不幸的是,推迟是有原因的。我的意思是.. 在我提供的 java 示例中,他们给出了 Thread.sleep(500ms)。这样当其他线程花费很长时间时,线程就会混合。 (如果不存在延迟 - 即使有多线程 - 它们很可能会按顺序执行。)强制执行延迟很重要,以便线程混合。然后,当给定关键字时,即使有足够的时间进行并发执行,它们也会等待。你明白我的意思吗?
    • @besabestin 你只需要在推迟它时移动,请参阅我编辑的答案。抱歉,我一开始没有说清楚。
    • @besabestin 如果您从对象设计的角度考虑,TestThread 应该可以控制何时将异步运行推迟到 TheDemo,因为 TestThread 负责时间在它自己的个人世界中(后台线程)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-04
    • 1970-01-01
    • 1970-01-01
    • 2020-07-13
    • 2010-09-13
    相关资源
    最近更新 更多