ios Objective-C中的同步和异步调用与多线程有什么区别?

gmol1639  于 2023-01-27  发布在  iOS
关注(0)|答案(7)|浏览(156)

很长一段时间以来,我认为异步是在后台线程上运行的同义词,而同步意味着在主线程上运行(阻止UI更新和交互)。我理解不在主线程上运行昂贵的操作是因为它不允许UI操作在主线程被占用时发生,但为什么同步会很麻烦呢?
但是,我后来注意到可以在主线程上进行异步调用,在后台线程上进行同步调用。
我经常听到人们说不要在同步或主线程上使用昂贵的调用,因为它会阻塞用户的UI。这是两个不同的问题,我应该确保我不这样做吗?有什么区别?

vwhgwdsa

vwhgwdsa1#

同步调用某个操作时,意味着启动该操作的线程将等待任务完成后再继续。异步意味着它不会等待。
话虽如此,当人们建议你异步执行一些缓慢或昂贵的进程时,他们暗示你不仅应该异步运行它,而且应该在后台线程上运行它。目标是释放主线程,以便它可以继续响应用户界面(而不是冻结),所以你是在异步地将任务分派给后台线程。
因此,这需要两个部分:首先,以GCD为例,获取一个后台队列(获取全局后台队列之一,或者创建自己的队列):

// one of the global concurrent background queues

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// or you could create your own serial background queue:
//
// dispatch_queue_t queue = dispatch_queue_create("com.domain.app.queuename", 0);

第二,将任务异步分派到该队列:

dispatch_async(queue, ^{
    // the slow stuff to be done in the background
});

操作队列的模式非常相似。创建一个操作队列并向该队列添加操作。
实际上,同步与异步的区别与主队列与后台队列的区别完全不同,但是当人们谈论“异步运行一些慢的进程”时,他们实际上是在说“在后台队列上异步运行一些慢的进程”。

gcuhipw9

gcuhipw92#

**“同步”本质上意味着“按顺序"。**基本上,当您执行同步操作时,后面的所有操作都必须等待操作完成后才能开始。

相反,**“异步”或多或少意味着“不按顺序"。**当您异步执行某项操作时,下面的代码可以立即运行,并且异步操作将在某个时间运行。它可能与其他线程上的其余代码并行运行。它可能只是被安排在同一线程上的其他时间运行。
同步性的概念本质上与特定的线程无关,它只是关于你是否必须等待一个操作完成。
主要的线索是可可AppKit在主线程上运行主事件循环,因此如果主线程正在等待操作完成,它就不能处理任何输入或更新UI。不过,如果您有一段代码在后台线程上运行,则运行同步代码 * 不会阻塞主事件循环 *,因为等待同步操作完成的不是主线程。
类似地,从后台线程长时间运行的异步操作放在主线程上 * 可能 * 会导致问题,因为虽然后台线程不会等待操作完成,但它仍然会占用主线程上需要运行事件循环的时间。

myss37ts

myss37ts3#

让我们举一些简单的例子:

多线程异步调用:

// Methods gets called in different thread and does not block the current thread. 
[NSURLConnection sendAsynchronousRequest:request 
                                   queue:queue 
                       completionHandler:
    ^(NSURLResponse *response, NSData *data, NSError *error) {
}];

多线程同步调用:

//Do something
dispatch_sync(queue, ^{
    //Do something else
});
//Do More Stuff

这里有//Do something //Do something else和//Do More things,它们是连续完成的,即使//Do something else是在不同的线程上完成的。
通常情况下,当人们使用不同的线程,整个目的是为了让一些事情可以得到执行,而无需等待。假设你想下载大量的数据,但你想保持界面流畅。
因此,dispatch_sync很少被使用。但是它就在那里。我个人从来没有使用过它。为什么不要求一些使用dispatch_sync的示例代码或项目呢?

使用一个线程的异步调用:

[self performSelector:@selector(doSomething) withObject:nil afterDelay:0];

此处显示要在调用“doSomething”之前完成的当前runloop。换句话说,当前调用堆栈可以在调用“doSomething”之前完成(当前方法返回)。

与一个线程同步调用:

[self doSomething];

我觉得你不需要解释。
一般来说,异步活动与线程不同,但是在iOS中,它们是使用这种方式实现的。并非所有语言都是如此。我们通常使用运行循环来管理不同的异步任务。

dpiehjr4

dpiehjr44#

swift 3,4,4,2***Synchronous***表示启动该操作的线程将等待任务完成后再继续。

DispatchQueue.main.sync {

}

***异步***表示在后台完成任务并在完成时通知您,表示不会等待。

DispatchQueue.main.async {

}
fcg9iug3

fcg9iug35#

异步意味着离线,同步意味着在线。您可以执行同步任务并同时阻塞多个线程。
如果你在一个后台线程中,想要更新一大堆用户界面,你可以调用dispatch队列中的主线程。如果你调用dispatch_sync,那么你当前所在的代码将等待dispatch完成,从而阻塞你所在的后台线程,并在它更新主线程时阻塞用户界面。
但是,如果调用dispatch_async,后台线程将继续执行列出的其余代码,而主线程将运行请求的dispatch块。
在主线程中也是如此。如果你从主线程调用一个dispatch_sync到一个全局或自定义队列中,当主线程在一个单独的线程中运行代码时,它会阻塞主线程。我不能说我知道有这样的情况,但这肯定是可能的。
当你有计算代码,网络服务代码,获取代码,诸如此类的不影响用户界面的东西时,最好在一个单独的线程中完成。对于这类东西,我会在一个全局线程中执行一个dispatch_async。然后当代码完成时,我会在主线程中运行一个dispatch_async,告诉它用我刚刚计算的东西更新用户界面。
同步意味着阻塞,异步意味着它将在以后某个时间(可能是现在)完成,而不会阻塞您当前正在执行的操作。

sycxhyv7

sycxhyv76#

下面的讨论很好地回答了这个问题:Asynchronous vs Multithreading - Is there a difference?
在一般情况下,异步调用不一定创建新线程。这是实现它的一种方式,而预先存在的线程池或外部进程是其他方式。它严重依赖于语言、对象模型(如果有的话)和运行时环境。
异步只是意味着调用线程不会等待响应,在调用线程中也不会发生异步活动。
因此,基本上,在等待加载某些内容时可能会发生其他活动,但这些活动可能在单独的线程上完成,也可能不在单独的线程上完成。

mum43rcc

mum43rcc7#

    • 同步与异步**

同步和异步操作是关于下一个任务相对于当前任务的执行顺序。
让我们看一个例子,我们有三个任务(任务1、任务2、任务3),我们将通过任务2进行操作。任务是一个原子操作-堆栈(方法框架)中的方法调用。

    • 同步**

意味着任务将一个接一个地执行,当前任务完成后才开始下一个任务,任务2完成后才开始任务3。

    • 同步+单线程=顺序**

如果从主线程调用DispatchQueue.main.sync,则会导致死锁

main() {
    task1()
    task2()
    task3()
}

    • 同步+多线程=并行**

阻塞意味着线程只是在等待(尽管它可以做一些有用的事情,例如Java ExecutorService About(https://stackoverflow.com/a/66567556/4770877)和Future About(https://stackoverflow.com/a/66082407/4770877))
DispatchQueue.global().sync()

main() {
    task1()
    Future future = ExecutorService.submit(task2())
    future.get() //<- blocked operation
    task3()
}

    • 异步**

暗示任务立即返回控制,并承诺执行代码并在以后通知结果(例如回调、功能)。即使任务2未完成,也会执行任务3。异步回调,完成处理程序About(https://stackoverflow.com/a/60990583/4770877)

    • 异步+单线程=并发**

使用回调队列(消息队列)和事件循环(RunLoop、Looper)About(https://stackoverflow.com/a/75204210/4770877)。事件循环检查线程堆栈是否为空,如果为真,则将第一个项目从回调队列推入线程堆栈,并再次重复这些步骤。简单的示例是按钮单击、发布事件...
Timer.scheduledTimer(withTimeInterval: 2, repeats: false)

main() {
    task1()
    ThreadMain.handler.post(task2());
    task3()
}

    • 异步+多线程=并发和并行**

例如,当你需要在另一个线程上进行一些计算而不阻塞时,你可以使用阻塞方法get()或通过循环使用异步回调来使用任务2的结果。
DispatchQueue.global().async()

main() {
    task1()

    new Thread(task2()).start();
    //or
    Future future = ExecutorService.submit(task2())

    task3()
}

    • 示例**

例如,在移动世界,我们有UI/主线程,我们需要下载的东西,我们有几个选项:

  • 同步块-阻止用户界面线程并等待下载完成。2用户界面没有响应。
  • 异步回调-创建一个新线程,用异步回调来更新UI(不可能从非UI线程访问UI)。
  • 异步协同程序About(https://stackoverflow.com/a/65169350/4770877)-异步任务与同步语法。它允许混合下载任务(暂停功能)与UI任务。

Concurrency vs Parallelism(https://stackoverflow.com/a/63744581/4770877)
iOS GCD(https://stackoverflow.com/a/61102990/4770877)
DispatchQueue sync vs async(https://stackoverflow.com/a/59542492/4770877)

相关问题