Qt 线程基础(QThread.QtConcurrent.QThreadPool等)

2012-11-08  来源:本站原创  分类:Qt  人气:11 

使用线程

基本上有种使用线程的场合:

  • 通过利用处理器的多个核使处理速度更快。
  • 为保持GUI线程或其他高实时性线程的响应,将耗时的操作或阻塞的调用移到其他线程。

何时使用其他技术替代线程

开发人员使用线程时需要非常小心。启动线程是很容易的,但确保所有共享数据保持一致很难。遇到问题往往很难解决,这是由于在一段时间内它可能只出现一次或只在特定的硬件配置下出现。在创建线程来解决某些问题之前,应该考虑一些替代的技术


替代技术


注解


QEventLoop::processEvents()


在一个耗时的计算操作中反复调用QEventLoop::processEvents() 可以防止界面的假死。尽管如此,这个方案可伸缩性并不太好,因为该函数可能会被调用地过于频繁或者不够频繁。


QTimer


后台处理操作有时可以方便地使用Timer安排在一个在未来的某一时刻执行的槽中来完成。在没有其他事件需要处理时,时间隔为0的定时器超时事件被相应


QSocketNotifier
QNetworkAccessManager
QIODevice::readyRead()


这是一个替代技术,替代有一个或多个线程在慢速网络执行阻塞读的情况。只要响应部分的计算可以快速执行,这种设计比在线程中实现的同步等待更好。与线程相比这种设计更不容易出错且更节能(energy efficient)。在许多情况下也有性能优势。

一般情况下,建议只使用安全和经过测试的方案而避免引入特设线程的概念。QtConcurrent 提供了一个将任务分发到处理器所有的核的易用接口。线程代码完全被隐藏在 QtConcurrent 框架下,所以你不必考虑细节。尽管如此,QtConcurrent 不能用于线程运行时需要通信的情况,而且它也不应该被用来处理阻塞操作。

应该使用 Qt 线程的哪种技术?

有时候,你需要的不仅仅是在另一线程的上下文中运行一个函数。您可能需要有一个生存在另一个线程中的对象来为 GUI线程提供服务。也许你想在另一个始终运行的线程中来轮询硬件端口并在有关注的事情发生时发送信号到GUI线程。Qt为开发多线程应用程序提供了多种 不同的解决方案。解决方案的选择依赖于新线程的目的以及线程的生命周期。


生命周期


开发任务


解决方案


一次调用


在另一个线程中运行一个函数,函数完成时退出线程


编写函数,使用QtConcurrent::run 运行它


派生QRunnable,使用QThreadPool::globalInstance()->start()运行它


派生QThread,重新实现QThread::run() ,使用QThread::start()运行它


一次调用


需要操作一个容器中所有的项。使用处理器所有可用的核心。一个常见的例子是从图像列表生成缩略图。


QtConcurrent 提供了map()函你数来将操作应用到容器中的每一个元素,提供了fitler()函数来选择容器元素,以及指定reduce函数作为选项来组合剩余元素。


一次调用


一个耗时运行的操作需要放入另一个线程。在处理过程中,状态信息需要发送会GUI线程。


使用QThread,重新实现run函数并根据需要发送信号。使用信号槽的queued连接方式将信号连接到GUI线程的槽函数。


持久运行


生存在另一个线程中的对象,根据要求需要执行不同的任务。这意味着工作线程需要双向的通讯。


派生一个QObject对象并实现需要的信号和槽,将对象移动到一个运行有事件循环的线程中并通过queued方式连接的信号槽进行通讯。


持久运行


生存在另一个线程中的对象,执行诸如轮询端口等重复的任务并与GUI线程通讯。


同上,但是在工作线程中使用一个定时器来轮询。尽管如此,处理轮询的最好的解决方案是彻底避免它。有时QSocketNotifer是一个替代。

Qt线程基础

QThread是一个非常便利的跨平台的对平台原生线程的抽象。启动一个线程是很简单的。让我们看一个简短的代码:生成一个在线程内输出"hello"并退出的线程。

// hellothread/hellothread.h  class HelloThread : public QThread  {      Q_OBJECT  private:      void run();  };

我们从QThread派生出一个类,并重新实现run方法。

// hellothread/hellothread.cpp  void HelloThread::run()  {       qDebug() << "hello from worker thread " << thread()->currentThreadId();  }

run方法中包含将在另一个线程中运行的代码。在本例中,一个包含线程ID的消息被打印出来。 QThread::start() 将在另一个线程中被调用。

int main(int argc, char *argv[])  {      QCoreApplication app(argc, argv);      HelloThread thread;      thread.start();      qDebug() << "hello from GUI thread " << app.thread()->currentThreadId();      thread.wait();  // do not exit before the thread is completed!      return 0;  }

QObject与线程

QObject有线程关联(thread affinity)[如何翻译?关联?依附性?dbzhang800 20110618],换句话说,它生存于一个特定的线程。这意味着,在创建时QObject保存了到当前线程的指针。当事件使用postEvent()被 派发时,这个信息变得很有用。事件被放置到相应线程的事件循环中。如果QObject所依附的线程没有事件循环,该事件将永远不会被传递。

要启动事件循环,必须在run()内调用exec()。线程关联可以通过moveToThread()来更改。

如上所述,当从其他线程调用对象的方法时开发人员必须始终保持谨慎。线程关联不会改变这种状况。 Qt文档中将一些方法标记为线程安全。postEvent()就是一个值得注意的例子。一个线程安全的方法可以同时在不同的线程被调用。

通常情况下并不会并发访问的一些方法,在其他线程调用对象的非线程安全的方法在出现造成意想不到行为的并发访问前 数千次的访问可能都是工作正常的。编写测试代码不能完全确保线程的正确性,但它仍然是重要的。在Linux上,Valgrind和Helgrind有助于 检测线程错误。

QThread的内部结构非常有趣:

  • QThread并不生存于执行run()的新线程内。它生存于旧线程中。
  • QThread的大多数成员方法是线程的控制接口,并设计成从旧线程中被调用。不要使用moveToThread()将该接口移动到新创建的线程中;调用moveToThread(this)被视为不好的实践。
  • exec()和静态方法usleep()、msleep()、sleep()要在新创建的线程中调用。
  • QThread子类中定义的其他成员可在两个线程中访问。开发人员负责访问的控制。一个典型的策略是在start()被调用前设置成员变量。一旦 工作线程开始运行,主线程不应该操作其他成员。当工作线程终止后,主线程可以再次访问其他成员。这是一个在线程开始前传递参数并在结束后收集结果的便捷的 策略。

QObject必须始终和parent在同一个线程。对于在run()中生成的对象这儿有一个惊人的后果:

void HelloThread::run()  {       QObject *object1 = new QObject(this);  //error, parent must be in the same thread       QObject object2;  // OK       QSharedPointer <QObject> object3(new QObject); // OK  }

使用互斥量保护数据的完整

互斥量是一个拥有lock()和unlock()方法并记住它是否已被锁定的对象。互斥量被设计为从多个线程调 用。如果信号量未被锁定lock()将立即返回。下一次从另一个线程调用会发现该信号量处于锁定状态,然后lock()会阻塞线程直到其他线程调用 unlock()。此功能可以确保代码段将在同一时间只能由一个线程执行。

使用事件循环防止数据破坏

Qt的事件循环对线程间的通信是一个非常有价值的工具。每个线程都可以有它自己的事件循环。在另一个线程中调用一个槽的一个安全的方法是将调用放置到另一个线程的事件循环中。这可以确保目标对象调用另一个的成员函数之前可以完成当前正在运行的成员函数。

那么,如何才能把一个成员调用放于一个事件循环中? Qt的有两种方法来做这个。一种方法是通过queued信号槽连接;另一种是使用QCoreApplication::postEvent()派发一个事 件。queued的信号槽连接是异步执行的信号槽连接。内部实现是基于posted的事件。信号的参数放入事件循环后信号函数的调用将立即返回。

连接的槽函数何时被执行依赖于事件循环其他的其他操作。

通过事件循环通信消除了我们使用互斥量时所面临的死锁问题。这就是我们为什么推荐使用事件循环,而不是使用互斥量锁定对象的原因。

处理异步执行

一种获得一个工作线程的结果的方法是等待线程终止。在许多情况下,一个阻塞等待是不可接受的。阻塞等待的替代方法 是异步的结果通过posted事件或者queued信号槽进行传递。由于操作的结果不会出现在源代码的下一行而是在位于源文件其他部分的一个槽中,这会产 生一定的开销,因为,但在位于源文件中其他地方的槽。 Qt开发人员习惯于使用这种异步行为工作,因为它非常相似于GUI程序中使用的的事件驱动编程。

***************************************************************************************************

一、QThreadPool类

  QThreadPool管理一组线程。它负责管理和回收单个QThread对象以减少程序中线程创建的开销。每个Qt应用程序都有一个全局的QThreadPool对象,可通过方法globalInstance()获得。为了调用QThreadPool中的一个线程,需要提供一个从QRunnable继承过来的类,并实现其中的run方法。然后创建一个该类的对象,传递给QThreadPool::start()方法。代码片断如下:

[cpp] view plain copy

  1. class HelloWorldTask : public QRunnable
  2. {
  3. void run()
  4. {
  5. qDebug() << "Hello world from thread" << QThread::currentThread();
  6. }
  7. }
  8. HelloWorldTask *hello = new HelloWorldTask();
  9. // QThreadPool takes ownership and deletes 'hello' automatically
  10. QThreadPool::globalInstance()->start(hello);

默认情况下, QThreadPool自动删除QRunnable对象。使用QRunnable::setAutoDelete()方法可以改变该默认行为。QThreadPool支持在QRunnable::run方法中通过调用tryStart(this)来多次执行相同的QRunnable。当最后一个线程退出run函数后,如果autoDelete启用的话,将删除QRunnable对象。在autoDelete启用的情况下,调用start()方法多次执行同一QRunnable会产生竞态,就避免这样做。

  那些在一定时间内会使用的线程将会过期。默认的过期时间是30秒。可通过setExpiryTimeout()方法来设置。设置一个负数的超时值代表禁用超时机制。方法maxThreadCount()可以查询可使用的最大线程数,你也可以设置最大的线程数。activeThreadCount反应的是当前正在被使用中的线程数个数。reserveThread函数保留某个线程为外部使用,releaseThread释放该线程,这样就可以被再次使用。

二、QtConcurrent命名空间

QtConcurrent命名空间里提供了一些高级API,利用这些API可以编写多线程程序,而不用直接使用比较低级的一些类,如mutext,lock, waitcondition以及semaphore等。使用QtConcurrent命令空间的API编写的程序会根据处理器的数目自动地调整线程的个数。QtConcurrent包含了用于并行列表处理的函数式编程,包含实现共享内存系统的MapReduce和FilterReduce, 以及管理GUI应用程序中异步计算的类。相关的类说明如下:


QtConcurrent::map()


appliesa function to every item in a container, modifying the itemsin-place


QtConcurrent::mapped()


islike map(), except that it returns a new container with themodifications


QtConcurrent::mappedReduced()


islike mapped(), except that the modified results are reduced orfolded into a single result.


QtConcurrent::filter()


litems from a container based on the result of a filter function.


QtConcurrent::filtered()


islike filter(), except that it returns a new container with thefiltered results


QtConcurrent::filteredReduced()


islike filtered(), except that the filtered results are reduced orfolded into a single result


QtConcurrent::run()


runsa function in another thread.


QFutureIterator


allowsiterating through results available via QFuture.


QFutureWatcher


allowsmonitoring a QFuture usingsignals-and-slots.


QFutureSynchronizer


isa convenience class that automatically synchronizes severalQFutures.

代码实例:

[cpp] view plain copy

  1. using namespace QtConcurrent;
  2. void hello(QString name)
  3. {
  4. qDebug() << "Hello" << name << "from" << QThread::currentThread();
  5. }
  6. int main(int argc, char **argv)
  7. {
  8. QApplication app(argc, argv);
  9. QFuture<void> f1 = run(hello, QString("Alice"));
  10. QFuture<void> f2 = run(hello, QString("Bob"));
  11. f1.waitForFinished();
  12. f2.waitForFinished();
  13. return app.exec();
  14. }

**********************************************************************************************************

QtConcurrent::run() 的使用

2011-11-25 10:36 155人阅读 评论(0) 收藏 举报

QFuture<T> run(const Class *object, T (Class::*fn)(Param1, Param2, Param3, Param4, Param5) const, const Arg1 &arg1,const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5)

run()函数的原型如上,此函数是QtConcurrent命名空间里的函数.主要功能是令启动一个线程来执行一个函数.Concurrent的英文示意就是并发的意思.

下面简要的介绍run()函数的使用方法:

1.首先要有一个需要在另外开启的线程中执行的函数:

void thread_add(QObject *receiver,int a,int b)

{
 QString message=QString("%1 + %2 = %3").arg(a).arg(b).arg(a+b);
 QApplication::postEvent(receiver,new ProgressEvent(true, message));
}

函数在线程中运行完毕后会向receiver发送一个消息,来返回结果.

2.有了要在线程中运行的函数,再来看看怎么启动线程来运行这个函数

void MainWindow::on_pushButton_clicked()

{
 for(int i=0;i<9;i++)
 for(int j=0;j<9;j++)
 QtConcurrent::run(thread_add,this,i,j);
}

点击一个按钮就会运行这段代码,然后启动8*8=64个线程,线程要运行的函数就是thread_add(之前定义的),消息接收对象就是MainWindow这个类的实例

3.线程得到了运行会发送消息给MainWindow,MainWindow重新实现bool MainWindow::event ( QEvent * event )处理接收到的消息,并显示出来

bool MainWindow::event ( QEvent * event )

{
 if (event->type() ==
 static_cast<QEvent::Type>(ProgressEvent::EventId)) {
 ProgressEvent *progressEvent =
 static_cast<ProgressEvent*>(event);
 Q_ASSERT(progressEvent);
 ui->teLog->append(progressEvent->message);
 return true;
 }
 return QMainWindow::event(event);
}

再给出自定义的消息结构

struct ProgressEvent : public QEvent

{
 enum {EventId = QEvent::User};
 explicit ProgressEvent(bool saved_, const QString &message_)
 : QEvent(static_cast<Type>(EventId)),
 saved(saved_), message(message_) {}
 const bool saved;
 const QString message;
};
相关文章
  • Qt 线程基础(QThread.QtConcurrent.QThreadPool等) 2012-11-08

    使用线程 基本上有种使用线程的场合: 通过利用处理器的多个核使处理速度更快. 为保持GUI线程或其他高实时性线程的响应,将耗时的操作或阻塞的调用移到其他线程. 何时使用其他技术替代线程 开发人员使用线程时需要非常小心.启动线程是很容易的,但确保所有共享数据保持一致很难.遇到问题往往很难解决,这是由于在一段时间内它可能只出现一次或只在特定的硬件配置下出现.在创建线程来解决某些问题之前,应该考虑一些替代的技术 : 替代技术 注解 QEventLoop::processEvents() 在一个耗时的计

  • Qt 线程基础(QThread.QtConcurrent等) 2012-11-10

    昨晚看Qt的Manual,突然发现下一个版本的Qt中(Qt4.7.4.Qt4.8等)增加了一个特赞的介绍多线程的文章 : Thread Basics 注意: 该链接以后会失效,但是 到时候你直接看Qt自带Manual就行了 本文不是严格的翻译 dbzhang800 2011.06.18 使用线程 基本上有种使用线程的场合: 通过利用处理器的多个核使处理速度更快. 为保持GUI线程或其他高实时性线程的响应,将耗时的操作或阻塞的调用移到其他线程. 何时使用其他技术替代线程 开发人员使用线程时需要非常

  • Qt线程基础(这个比较完整) 2012-11-09

    线程基础 保谓线程? 线程与并行处理任务息息相关,就像进程一样.那么,线程与进程有什么区别呢?当你在电子表格上进行数据结算的时候,在相同的桌面上可能有一个播放器正在播放你最喜欢的歌曲.这是一个两个进程并行工作的例子:一个进程运行电子表格程序;另一个进程运行一个媒体播放器.这种情况最适合用多任务这个词来描述.进一步观察媒体播放器,你会发现在这个进程内,又存在并行的工作.当媒体播放器向音频驱动发送音乐数据的时候,用户界面上与之相关的信息不断地进行更新.这就是单个进程内的并行线程. 那么,线程的并行性

  • .NET 线程基础的使用介绍 2014-11-18

    本篇文章介绍了,.NET 线程基础的使用说明,需要的朋友参考下 线程作用及开销 早期计算机一次只能运行一个程序,长时间执行程序容易出现计算机"瘫痪"的问题,如果程序进入死循环则只能重启系统.即使计算机不崩溃,也难免让用户崩溃.为了解决这个问题,操作系统设计者设计出了进程的概念,使得每个应用程序运行在一个虚拟的内存空间中.进程中又包含多个线程,CPU则根据操作系统调度执行每个进程中的线程任务.通过线程这种对CPU的虚拟化管理方式,操作系统形成了多任务执行的机制.但与一切虚拟化机制一样,线

  • qt线程----部分代码片段 2012-11-09

    一.把视频显示到界面的方法 (1)针对qt4的(视频格式为rgb32) v4l_grab_movie(&v4l_dev); unsigned char *pBuffer= v4l_dev.buffer; QImage image(pBuffer,320,240,QImage::Format_RGB32); QPixmap pixmap; pixmap=pixmap.fromImage(image); label->setPixmap(pixmap); label->setFixedSi

  • Qt线程和定时器[一] 2014-08-14

    新的线程run里面一定要有exec的调用,否则无法接受消息的. class myQThr : public QThread { Q_OBJECT public: myQThr(QObject *in = NULL) :QThread(in) { WrTimer = new QTimer(this); connect(WrTimer, SIGNAL(timeout()), this, SLOT(TimerOutWr1()), Qt::DirectConnection); WrTimer->star

  • QT线程与定时器[二] 2014-08-14

    下面转一个相关说明吧 实例代码1: class MThread :public QThread { public: MThread(); ~MThread(); virtual void run(); void foo(); ... }; POINT 1:QThread类的实例与普通类的实例没什么不同,只是运行着的run()函数会不同 实例代码2: class MDialog :public QDialog { ... MThread *mythread; }; MDialog::MDialog

  • Qt下几个线程函数介绍 2012-11-09

    1.QCoreApplication::processEvents() 2.每个线程可以有它的事件循环,初始线程开始它的事件循环需使用QCoreApplication::exec(),别的线程开始它的事件循环需要用QThread::exec().像QCoreApplication一样,QThreadr提供了exit(int)函数,一个quit() slot. 线程安全的函数QCoreApplication::postEvent(),在任何时候,给任何线程中的任何对象投递一个事件,事件会在那个创建

  • Qt的多线程编程注意事项 2014-04-01

    QT总的来说是一个易学易用的库, 但是QT的多线程使用确实容易犯错,尤其是结合上异步的网络访问,谁用谁知道.我觉得核心是一句话,QThread要当作线程控制块用,不要以为派生一个QThread的子类,里面的东西都跑在新的线程里了,只有run()里的是这样. 下面这篇文章我觉得讲的很好,深入解析QT的线程用法,文章有点长,但是值得一读. 原文在 http://qt-project.org/wiki/ThreadsEventsQObjects 译文在 http://www.cppblog.com/b

  • Qt::ConnectionType 解析 2014-08-14

    signal/slot在底层会使用三种方式传递消息.参见QObject::connect()方法: bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoCompatConnection ) 最后一个参数是就是传递消息的方式了,有四个取值: Qt::Di

  • QThread介绍(译) 2014-09-09

    QThread类提供了独立于平台的处理线程类. 在程序中,一个QThread对象处理一个线程.QThread通过start方法创建一个新线程,新线程调用run方法(所以在程序中看到的是run方法在新线程中运行.是否只有run方法运行在新线程中?).默认情况下,run方法通过调用exec方法启动事件循环,并在线程内运行Qt 事件循环.当退出run方法将会终止这个新线程. 你可以通过QObject::moveToThread方法将worker对象移动到某个线程中. class Worker : pu

  • JVM 并发性: Java 和 Scala 并发性基础 2015-01-03

    处理器速度数十年来一直持续快速发展,并在世纪交替之际走到了终点.从那时起,处理器制造商更多地是通过增加核心来提高芯片性能,而不再通过增加时钟速率来提高芯片性能.多核系统现在成为了从手机到企业服务器等所有设备的标准,而这种趋势可能继续并有所加速.开发人员越来越需要在他们的应用程序代码中支持多个核心,这样才能满足性能需求. 在本系列文章中,您将了解一些针对 Java 和 Scala 语言的并发编程的新方法,包括 Java 如何将 Scala 和其他基于 JVM 的语言中已经探索出来的理念结合在一起.

  • (转)Python线程指南 2012-02-21

    本文介绍了Python对于线程的支持,包括"学会"多线程编程需要掌握的基础以及Python两个线程标准库的完整介绍及使用示例. 注意:本文基于Python2.4完成,:如果看到不明白的词汇请记得百度谷歌或维基,whatever. 尊重作者的劳动,转载请注明作者及原文地址 >.< 1. 线程基础 1.1. 线程状态 线程有5种状态,状态转换的过程如下图所示: 1.2. 线程同步(锁) 多 线程的优势在于可以同时运行多个任务(至少感觉起来是这样).但是当线程需要共享数据时,可能

  • Python线程指南 2013-08-27

    1. 线程基础 1.1. 线程状态 线程有5种状态,状态转换的过程如下图所示: 1.2. 线程同步(锁) 多 线程的优势在于可以同时运行多个任务(至少感觉起来是这样).但是当线程需要共享数据时,可能存在数据不同步的问题.考虑这样一种情况:一个列表里所有元 素都是0,线程"set"从后向前把所有元素改成1,而线程"print"负责从前往后读取列表并打印.那么,可能线程"set"开始改的时候,线 程"print"便来打印列表了,输

  • 初级必备:Java版本区别与了解_JavaSE_线程小结 2014-11-28

    链接地址:http://www.xx566.com/detail/99.html java.lang.Thread类是传统java用来调度和管理线程的工具,其提供了多种方法来操控线程:从java 5开始,Java提供了Java.util.concurrent工具包用来进行并发编程,并引入了lock,其提供了Condition接 口,Condition实例实质被绑定在一个lock上,通过其提供的方法,也可以方便的对线程进行调度和管理,一系列眼花缭乱的方法:sleep(). join().yield

  • 阅读QtCreator--Concurrent预备知识 2012-07-19

    在QtCreator当中用到了不少的Concurrent(并发),比如编译时,搜索时等.其实在很多场合中都需要用到,一般是CPU去做一项大任务(花费较长时间)时相应用户操作.另一个重要用途就是在当前这个多核,甚至多CPU的年代,并行变成成为一种时尚了,它也确实提高了应用程序的性能.我的电脑是单CPU,2核心4线程,所以相比单应用程序,应该可以将性能提高将近4倍(当然不会是4倍的).我所听过的有很多库是这方面的,比如CUDA,OpenCL,OpenMP.Qt是怎么做的还真不知道,望高手指教.首先来

  • 我的专属QQ (三) 附源码 2012-02-14

    要源码的朋友太多了,满眼的邮箱地址,我很头疼.鉴于现在CSDN首页的Qt应用大赛正在火热进行中,我干脆把源码奉献出来,给大家参考一下好了.不过,这不是我一个人的劳动成果,我一直认为技术领域的最高境界是分享,所以希望得到你的尊重. 有什么好的意见和建议欢迎你提,但是请注意语气.我写博客的目的有两个,一是记录,二是分享.我记录我的学习历程,分享给大家.我不是神,我想神也不能精通各个技术领域,不是最好的方案你就冷嘲热讽,于情于理都不太合适吧.我这又不是出书,你掏腰包买了看了觉得不好,骂几句才痛快.我自

  • XMPP-IM方案分析 2014-07-16

    1. 概述 1.1 Jabber & Xmpp Jabber(XMPP- Extensible Messaging and Presence Protocol)协议,是一个开源的即时消息协议,建立在jabber协议上的IM客户端可以互连.关于XMPP的两个官方网http://www.xmpp.org/ 和http://www.jabber.org/. 支持jabber协议的IM软件非常多 其中包括开源不开源的.参见: http://www.jabber.org/clients(全部开源),主要以

  • C#转C++的一点分享 2014-04-27

    前几天看到这样一篇博客<那些年·我们读过的专业书籍>,里面列了很多大家认为很好的书,加上自己在自学C++的工程中也看了不少书,感觉并不是所有的书都值得花时间去看的,毕竟很多人一年下来也看不了2,3本书,不同的技术能力的人,适合看的书都不太一样,在这么多大家都认为是经典的书中,选出几本真正适合自己的才是王道,经典一多了,有些比起来就不是那么经典了,当然大家都说经典,自然有可看之处,如果有多余的时间,多看些书自然是好的. 下面是我看过的技术书籍(不一定看完),还有本<程序员的自我修养>

  • Objective-C并发编程:API和挑战 2014-07-20

    并发指的是在同一时间运行多个任务.在单核CPU的情况下,它通过分时的方式实现,如果有多个CPU可用,则是真正意义上的多个任务"并行"执行了. OS X和iOS提供了多个API支持并发编程.每个API都有自己特殊的功能和限制,适用于完成不同的任务.它们也分布在不同的抽象层次,我们可以通过底层API去做些非常接近硬件的底层操作,但这样的话,我们也需要做更多的事去保证一切运行正常. 并发编程是件非常棘手的事,有着许多复杂的问题和陷阱,而且在使用像GCD或NSOperationQueue这样的