1.进程和线程

   进程定义:进程是正在运行的程序的实例,进程是内核分配资源的最基本的单元,而线程是内核执行的最基本单元,进程内可以包含多个线程,只要记住这三个要点,就可以很清楚的理清进程和线程的行为模式。

  程序在运行的时候是需要操作系统分配内存和其他硬件资源的,所以将运行的程序抽象为进程,一开始操作系统只能执行单一的进程,后来使用分时间片运行多个进程产生了多任务系统

  而线程的出现,是由于进程开销比较大,还有就是进程之间的资源隔离,导致数据沟通复杂,所以产生线程。

  进程和进程之间是资源隔离的,所以一个进程崩溃不会影响其他进程,但是由于线程是包含进程之内的,线程的崩溃就会引发进程的崩溃,而在同一进程的线程也会继而崩溃。

1.1python Threading线程模块

  threading线程模块用于提供线程线相关的操作,线程是进程的最小单元。

threading 模块建立在 _thread 模块之上。thread 模块以低级、原始的方式来处理和控制线程,而 threading 模块通过对 thread 进行二次封装

import threading
import time def worker(num):
time.sleep(1)
print('the num is {s}'.format(s=num)) for i in range(20):
t=threading.Thread(target=worker,args=(i,))
t.start()

使用循环创建20个线程,交给cpu,cpu根据调度算法分时执行命令

Thread方法属性说明:

t.start() : 激活线程,

t.getName() : 获取线程的名称

t.setName() : 设置线程的名称

t.name : 获取或设置线程的名称

t.is_alive() : 判断线程是否为激活状态

t.isAlive() :判断线程是否为激活状态

t.setDaemon() 设置为后台线程或前台线程(默认:False);通过一个布尔值设置线程是否为守护线程,必须在执行start()方法之后才可以使用。如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止;如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止

t.isDaemon() : 判断是否为守护线程

t.ident :获取线程的标识符。线程标识符是一个非零整数,只有在调用了start()方法之后该属性才有效,否则它只返回None。

t.join() :逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义

t.run() :线程被cpu调度后自动执行线程对象的run方法

2、线程锁threading.RLock和threading.Lock

由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,CPU接着执行其他线程。为了保证数据的准确性,引入了锁的概念。所以,可能出现如下问题:

例:假设列表A的所有元素就为0,当一个线程从前向后打印列表的所有元素,另外一个线程则从后向前修改列表的元素为1,那么输出的时候,列表的元素就会一部分为0,一部分为1,这就导致了数据的不一致。锁的出现解决了这个问题。

import threading
import time globals_num = 0 lock = threading.RLock() def Func():
lock.acquire() # 获得锁
global globals_num
globals_num += 1
time.sleep(1)
print(globals_num)
lock.release() # 释放锁 for i in range(10):
t = threading.Thread(target=Func)
t.start()

3、threading.RLock和threading.Lock 的区别

RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。 如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的琐。

import threading
lock = threading.Lock() #Lock对象
lock.acquire()
lock.acquire() #产生了死琐。
lock.release()
lock.release()  import threading
rLock = threading.RLock() #RLock对象
rLock.acquire()
rLock.acquire() #在同一线程内,程序不会堵塞。
rLock.release()
rLock.release()

4、threading.Event

python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。

事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。

  • clear:将“Flag”设置为False
  • set:将“Flag”设置为True
  • Event.isSet() :判断标识位是否为Ture。
import threading

def do(event):
print('start')
event.wait()
print('execute') event_obj = threading.Event()
for i in range(10):
t = threading.Thread(target=do, args=(event_obj,))
t.start() event_obj.clear()
inp = input('input:')
if inp == 'true':
event_obj.set()

当线程执行的时候,如果flag为False,则线程会阻塞,当flag为True的时候,线程不会阻塞。它提供了本地和远程的并发性。

5、threading.Condition

一个condition变量总是与某些类型的锁相联系,这个可以使用默认的情况或创建一个,当几个condition变量必须共享和同一个锁的时候,是很有用的。锁是conditon对象的一部分:没有必要分别跟踪。

condition变量服从上下文管理协议:with语句块封闭之前可以获取与锁的联系。 acquire() 和 release() 会调用与锁相关联的相应的方法。

其他和锁关联的方法必须被调用,wait()方法会释放锁,当另外一个线程使用 notify() or notify_all()唤醒它之前会一直阻塞。一旦被唤醒,wait()会重新获得锁并返回,

Condition类实现了一个conditon变量。 这个conditiaon变量允许一个或多个线程等待,直到他们被另一个线程通知。 如果lock参数,被给定一个非空的值,,那么他必须是一个lock或者Rlock对象,它用来做底层锁。否则,会创建一个新的Rlock对象,用来做底层锁。

  • wait(timeout=None) : 等待通知,或者等到设定的超时时间。当调用这wait()方法时,如果调用它的线程没有得到锁,那么会抛出一个RuntimeError 异常。 wati()释放锁以后,在被调用相同条件的另一个进程用notify() or notify_all() 叫醒之前 会一直阻塞。wait() 还可以指定一个超时时间。

如果有等待的线程,notify()方法会唤醒一个在等待conditon变量的线程。notify_all() 则会唤醒所有在等待conditon变量的线程。

注意: notify()和notify_all()不会释放锁,也就是说,线程被唤醒后不会立刻返回他们的wait() 调用。除非线程调用notify()和notify_all()之后放弃了锁的所有权。

在典型的设计风格里,利用condition变量用锁去通许访问一些共享状态,线程在获取到它想得到的状态前,会反复调用wait()。修改状态的线程在他们状态改变时调用 notify() or notify_all(),用这种方式,线程会尽可能的获取到想要的一个等待者状态。 例子: 生产者-消费者模型,

import threading
import time
def consumer(cond):
with cond:
print("consumer before wait")
cond.wait()
print("consumer after wait") def producer(cond):
with cond:
print("producer before notifyAll")
cond.notifyAll()
print("producer after notifyAll") condition = threading.Condition()
c1 = threading.Thread(name="c1", target=consumer, args=(condition,))
c2 = threading.Thread(name="c2", target=consumer, args=(condition,)) p = threading.Thread(name="p", target=producer, args=(condition,)) c1.start()
time.sleep(2)
c2.start()
time.sleep(2)
p.start()

6、queue模块

Queue 就是对队列,它是线程安全的

举例来说,我们去麦当劳吃饭。饭店里面有厨师职位,前台负责把厨房做好的饭卖给顾客,顾客则去前台领取做好的饭。这里的前台就相当于我们的队列。形成管道样,厨师做好饭通过前台传送给顾客,所谓单向队列

这个模型也叫生产者-消费者模型。

import queue

q = queue.Queue(maxsize=0)  # 构造一个先进显出队列,maxsize指定队列长度,为0 时,表示队列长度无限制。

q.join()    # 等到队列为kong的时候,在执行别的操作
q.qsize() # 返回队列的大小 (不可靠)
q.empty() # 当队列为空的时候,返回True 否则返回False (不可靠)
q.full() # 当队列满的时候,返回True,否则返回False (不可靠)
q.put(item, block=True, timeout=None) # 将item放入Queue尾部,item必须存在,可以参数block默认为True,表示当队列满时,会等待队列给出可用位置,
                         为False时为非阻塞,此时如果队列已满,会引发queue.Full 异常。 可选参数timeout,表示 会阻塞设置的时间,过后,
                         如果队列无法给出放入item的位置,则引发 queue.Full 异常
q.get(block=True, timeout=None) # 移除并返回队列头部的一个值,可选参数block默认为True,表示获取值的时候,如果队列为空,则阻塞,为False时,不阻塞,
                      若此时队列为空,则引发 queue.Empty异常。 可选参数timeout,表示会阻塞设置的时候,过后,如果队列为空,则引发Empty异常。
q.put_nowait(item) # 等效于 put(item,block=False)
q.get_nowait() # 等效于 get(item,block=False)

代码如下:

#!/usr/bin/env python
import Queue
import threading message = Queue.Queue(10) def producer(i):
while True:
message.put(i) def consumer(i):
while True:
msg = message.get() for i in range(12):
t = threading.Thread(target=producer, args=(i,))
t.start() for i in range(10):
t = threading.Thread(target=consumer, args=(i,))
t.start()

那就自己做个线程池吧:

# 简单往队列中传输线程数
import threading
import time
import queue class Threadingpool():
def __init__(self,max_num = 10):
self.queue = queue.Queue(max_num)
for i in range(max_num):
self.queue.put(threading.Thread) def getthreading(self):
return self.queue.get() def addthreading(self):
self.queue.put(threading.Thread) def func(p,i):
time.sleep(1)
print(i)
p.addthreading() if __name__ == "__main__":
p = Threadingpool()
for i in range(20):
thread = p.getthreading()
t = thread(target = func, args = (p,i))
t.start()
#往队列中无限添加任务
import queue
import threading
import contextlib
import time StopEvent = object() class ThreadPool(object): def __init__(self, max_num):
self.q = queue.Queue()
self.max_num = max_num self.terminal = False
self.generate_list = []
self.free_list = [] def run(self, func, args, callback=None):
"""
线程池执行一个任务
:param func: 任务函数
:param args: 任务函数所需参数
:param callback: 任务执行失败或成功后执行的回调函数,回调函数有两个参数1、任务函数执行状态;2、任务函数返回值(默认为None,即:不执行回调函数)
:return: 如果线程池已经终止,则返回True否则None
""" if len(self.free_list) == 0 and len(self.generate_list) < self.max_num:
self.generate_thread()
w = (func, args, callback,)
self.q.put(w) def generate_thread(self):
"""
创建一个线程
"""
t = threading.Thread(target=self.call)
t.start() def call(self):
"""
循环去获取任务函数并执行任务函数
"""
current_thread = threading.currentThread
self.generate_list.append(current_thread) event = self.q.get() # 获取线程
while event != StopEvent: # 判断获取的线程数不等于全局变量 func, arguments, callback = event # 拆分元祖,获得执行函数,参数,回调函数
try:
result = func(*arguments) # 执行函数
status = True
except Exception as e: # 函数执行失败
status = False
result = e if callback is not None:
try:
callback(status, result)
except Exception as e:
pass # self.free_list.append(current_thread)
# event = self.q.get()
# self.free_list.remove(current_thread)
with self.work_state():
event = self.q.get()
else:
self.generate_list.remove(current_thread) def close(self):
"""
关闭线程,给传输全局非元祖的变量来进行关闭
:return:
"""
for i in range(len(self.generate_list)):
self.q.put(StopEvent) def terminate(self):
"""
突然关闭线程
:return:
"""
self.terminal = True
while self.generate_list:
self.q.put(StopEvent)
self.q.empty() @contextlib.contextmanager
def work_state(self):
self.free_list.append(threading.currentThread)
try:
yield
finally:
self.free_list.remove(threading.currentThread) def work(i):
print(i)
return i +1 # 返回给回调函数 def callback(ret):
print(ret) pool = ThreadPool(10)
for item in range(50):
pool.run(func=work, args=(item,),callback=callback) pool.terminate()
# pool.close()

multiprocessing是python的多进程管理包,和threading.Thread类似。

1、multiprocessing模块

直接从侧面用subprocesses替换线程使用GIL的方式,由于这一点,multiprocessing模块可以让程序员在给定的机器上充分的利用CPU。在multiprocessing中,通过创建Process对象生成进程,然后调用它的start()方法,

from multiprocessing import Process

def func(name):
print('hello', name) if __name__ == "__main__":
p = Process(target=func,args=('zhangyanlin',))
p.start()
p.join() # 等待进程执行完毕

在使用并发设计的时候最好尽可能的避免共享数据,尤其是在使用多进程的时候。 如果你真有需要 要共享数据, multiprocessing提供了两种方式。

(1)multiprocessing,Array,Value

数据可以用Value或Array存储在一个共享内存地图里,如下:

from multiprocessing import Array,Value,Process

def func(a,b):
a.value = 3.333333333333333
for i in range(len(b)):
b[i] = -b[i] if __name__ == "__main__":
num = Value('d',0.0)
arr = Array('i',range(11)) c = Process(target=func,args=(num,arr))
d= Process(target=func,args=(num,arr))
c.start()
d.start()
c.join()
d.join() print(num.value)
for i in arr:
print(i)<br>输出:<br>  3.1415927<br>  [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]

(2)multiprocessing,Manager

由Manager()返回的manager提供list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array类型的支持

from multiprocessing import Process,Manager
def f(d,l):
d["name"] = "zhangyanlin"
d["age"] = 18
d["Job"] = "pythoner"
l.reverse() if __name__ == "__main__":
with Manager() as man:
d = man.dict()
l = man.list(range(10)) p = Process(target=f,args=(d,l))
p.start()
p.join() print(d)
print(l)

Server process manager比 shared memory 更灵活,因为它可以支持任意的对象类型。另外,一个单独的manager可以通过进程在网络上不同的计算机之间共享,不过他比shared memory要慢。

2、进程池(Using a pool of workers)

Pool类描述了一个工作进程池,他有几种不同的方法让任务卸载工作进程。

进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。

我们可以用Pool类创建一个进程池, 展开提交的任务给进程池。 例:

from  multiprocessing import Pool
import time def f1(i):
time.sleep(0.5)
print(i)
return i + 100 if __name__ == "__main__":
pool = Pool(5)
for i in range(1,31):
pool.apply(func=f1,args=(i,)) #apply_async
def f1(i):
time.sleep(0.5)
print(i)
return i + 100
def f2(arg):
print(arg) if __name__ == "__main__":
pool = Pool(5)
for i in range(1,31):
pool.apply_async(func=f1,args=(i,),callback=f2)
pool.close()
pool.join()

一个进程池对象可以控制工作进程池的哪些工作可以被提交,它支持超时和回调的异步结果,有一个类似map的实现。

  • processes :使用的工作进程的数量,如果processes是None那么使用 os.cpu_count()返回的数量。
  • initializer: 如果initializer是None,那么每一个工作进程在开始的时候会调用initializer(*initargs)。
  • maxtasksperchild:工作进程退出之前可以完成的任务数,完成后用一个心的工作进程来替代原进程,来让闲置的资源被释放。maxtasksperchild默认是None,意味着只要Pool存在工作进程就会一直存活。
  • context: 用在制定工作进程启动时的上下文,一般使用 multiprocessing.Pool() 或者一个context对象的Pool()方法来创建一个池,两种方法都适当的设置了context

注意:Pool对象的方法只可以被创建pool的进程所调用。

New in version 3.2: maxtasksperchild

New in version 3.4: context

进程池的方法

  • apply(func[, args[, kwds]]) :使用arg和kwds参数调用func函数,结果返回前会一直阻塞,由于这个原因,apply_async()更适合并发执行,另外,func函数仅被pool中的一个进程运行。

  • apply_async(func[, args[, kwds[, callback[, error_callback]]]]) : apply()方法的一个变体,会返回一个结果对象。如果callback被指定,那么callback可以接收一个参数然后被调用,当结果准备好回调时会调用callback,调用失败时,则用error_callback替换callback。 Callbacks应被立即完成,否则处理结果的线程会被阻塞。

  • close() : 阻止更多的任务提交到pool,待任务完成后,工作进程会退出。

  • terminate() : 不管任务是否完成,立即停止工作进程。在对pool对象进程垃圾回收的时候,会立即调用terminate()。

  • join() : wait工作线程的退出,在调用join()前,必须调用close() or terminate()。这样是因为被终止的进程需要被父进程调用wait(join等价与wait),否则进程会成为僵尸进程。

  • map(func, iterable[, chunksize])¶

  • map_async(func, iterable[, chunksize[, callback[, error_callback]]])¶

  • imap(func, iterable[, chunksize])¶

  • imap_unordered(func, iterable[, chunksize])

  • starmap(func, iterable[, chunksize])¶

  • starmap_async(func, iterable[, chunksize[, callback[, error_back]]])

2.并发与并行

  并发和并行是得从物理硬件方面考虑,在以前电脑是单核时代,一个cpu核心只能进行单个任务

  将单个cpu运行时间分配给多个任务运行,这就是分时系统,就是并发,看起来是多个任务同时执行,本质还是一个任务一个任务的执行

  并行,就是在多核cpu时代,为每个cpu核心分配一个任务,是真正的多任务执行

3.协程

线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。

协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。

协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;

event loop是协程执行的控制点, 如果你希望执行协程, 就需要用到它们。

event loop提供了如下的特性:

  • 注册、执行、取消延时调用(异步函数)
  • 创建用于通信的client和server协议(工具)
  • 创建和别的程序通信的子进程和协议(工具)
  • 把函数调用送入线程池中

协程示例:

总结

import asyncio

async def cor1():
print("COR1 start")
await cor2()
print("COR1 end") async def cor2():
print("COR2") loop = asyncio.get_event_loop()
loop.run_until_complete(cor1())
loop.close()

最后三行是重点。

  • asyncio.get_event_loop()  : asyncio启动默认的event loop
  • run_until_complete()  :  这个函数是阻塞执行的,知道所有的异步函数执行完成,
  • close()  :  关闭event loop。

1、greenlet

import greenlet

def fun1():
print("")
gr2.switch()
print("")
gr2.switch() def fun2():
print("")
gr1.switch()
print("") gr1 = greenlet.greenlet(fun1)
gr2 = greenlet.greenlet(fun2)
gr1.switch()

2、gevent

gevent属于第三方模块需要下载安装包

  • pip3 install --upgrade pip3

  • pip3 install gevent
import gevent

def fun1():
print("www.baidu.com") # 第一步
gevent.sleep(0)
print("end the baidu.com") # 第三步 def fun2():
print("www.zhihu.com") # 第二步
gevent.sleep(0)
print("end th zhihu.com") # 第四步 gevent.joinall([
gevent.spawn(fun1),
gevent.spawn(fun2),
])

遇到IO操作自动切换

import gevent
import requests def func(url):
print("get: %s"%url)
gevent.sleep(0)
date =requests.get(url)
ret = date.text
print(url,len(ret)) gevent.joinall([
gevent.spawn(func, 'https://www.python.org/'),
gevent.spawn(func, 'https://www.yahoo.com/'),
gevent.spawn(func, 'https://github.com/'),
])

参考链接

https://wangdashuaihenshuai.github.io/2015/10/17/%E7%BC%96%E7%A8%8B%E4%B8%AD%E7%9A%84%E8%BF%9B%E7%A8%8B%E3%80%81%E7%BA%BF%E7%A8%8B%E3%80%81%E5%8D%8F%E7%A8%8B%E3%80%81%E5%90%8C%E6%AD%A5%E3%80%81%E5%BC%82%E6%AD%A5%E3%80%81%E5%9B%9E%E8%B0%83/

http://blog.rainy.im/2016/04/07/python-thread-and-coroutine/

http://www.cnblogs.com/aylin/p/5601969.html

python-进程,线程,协程的更多相关文章

  1. Python 进程线程协程 GIL 闭包 与高阶函数(五)

    Python 进程线程协程 GIL 闭包 与高阶函数(五) 1 GIL线程全局锁 ​ 线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的 ...

  2. python -- 进程线程协程专题

    进程专栏 multiprocessing 高级模块 要让Python程序实现多进程(multiprocessing),我们先了解操作系统的相关知识. Unix/Linux操作系统提供了一个fork() ...

  3. python 进程 线程 协程

    并发与并行:并行是指两个或者多个事件在同一时刻发生:而并发是指两个或多个事件在同一时间间隔内发生.在单核CPU下的多线程其实都只是并发,不是并行. 进程是系统资源分配的最小单位,进程的出现是为了更好的 ...

  4. python进程/线程/协程

    一 背景知识 顾名思义,进程即正在执行的一个过程.进程是对正在运行程序的一个抽象. 进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一.操作系统的其他所 ...

  5. python 进程/线程/协程 测试

    # Author: yeshengbao # -- coding: utf-8 -- # @Time : 2018/5/24 21:38 # 进程:如一个人拥有分身(分数数最好为cpu核心数)几乎同时 ...

  6. Python并发编程系列之常用概念剖析:并行 串行 并发 同步 异步 阻塞 非阻塞 进程 线程 协程

    1 引言 并发.并行.串行.同步.异步.阻塞.非阻塞.进程.线程.协程是并发编程中的常见概念,相似却也有却不尽相同,令人头痛,这一篇博文中我们来区分一下这些概念. 2 并发与并行 在解释并发与并行之前 ...

  7. python自动化开发学习 进程, 线程, 协程

    python自动化开发学习 进程, 线程, 协程   前言 在过去单核CPU也可以执行多任务,操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换任务2,任务2执行0.01秒,在切换到任务3,这 ...

  8. 进程&线程&协程

    进程  一.基本概念 进程是系统资源分配的最小单位, 程序隔离的边界系统由一个个进程(程序)组成.一般情况下,包括文本区域(text region).数据区域(data region)和堆栈(stac ...

  9. 多道技术 进程 线程 协程 GIL锁 同步异步 高并发的解决方案 生产者消费者模型

    本文基本内容 多道技术 进程 线程 协程 并发 多线程 多进程 线程池 进程池 GIL锁 互斥锁 网络IO 同步 异步等 实现高并发的几种方式 协程:单线程实现并发 一 多道技术 产生背景 所有程序串 ...

  10. python的进程/线程/协程

    1.python的多线程 多线程就是在同一时刻执行多个不同的程序,然而python中的多线程并不能真正的实现并行,这是由于cpython解释器中的GIL(全局解释器锁)捣的鬼,这把锁保证了同一时刻只有 ...

随机推荐

  1. 关于IOS的唯一标识总结

    APPLE官方宣布在2013年5月后,使用 UUID的APP将不能通过审核,同时APPLE增加了广告标识符(IDFA)和IDFV. 1.有什么方法获取UUID? //CFUUID CFUUIDRef ...

  2. Visual Studio 2013中因Browser Link引起的Javascript错误

    参考文章:http://www.cnblogs.com/daxnet/archive/2013/10/27/3391146.html Browser Link功能会引起js异常.在调试右侧的三角下拉选 ...

  3. 链接的热键属性accesskey

    <a href="" accesskey="h"></a> 意思是按住Alt键+h,再按enter键就可以直接链接到HTML的目标网址中 ...

  4. 使用PowerShell解三道测试开发笔试题

    在网上看到了三道测试开发的笔试题,答案是用Python解的.这段时间正好在学PowerShell,练习一下:) 1. 验证邮箱格式 2. 获取URL的后缀名 3. 获取前一天时间或前一秒 我的解法是: ...

  5. Qt容器类——1. QList类、QLinkedList类和QVector类

    在开发一个较高性能需求的应用程序时,程序员会比较关注这些容器类的运行效率,表2.1列出了QList.QLinkedList和QVector容器的时间复杂度比较. 1.QList类 QList<T ...

  6. 从Swift3的标准库协议看面向协议编程(一)

    Swift中,大量内置类如Dictionary,Array,Range,String都使用了协议 先看看Hashable 哈希表是一种基础的数据结构.,Swift中字典具有以下特点:字典由两种范型类型 ...

  7. 一些NSArray,NSDictionary,NSSet相关的算法知识

    iOS编程当中的几个集合类:NSArray,NSDictionary,NSSet以及对应的Mutable版本,应该所有人都用过.只是简单使用的话,相信没人会用错,但要做到高效(时间复杂度)精确(业务准 ...

  8. 分享 rabbitMQ入门详解

    原文地址http://blog.csdn.net/cugb1004101218/article/details/21243927 目录(?)[-] rabbitMQ说明文档 rabbitMQ是什么 消 ...

  9. vim - save current file with a new name but keep editing current file

    http://superuser.com/questions/414110/vim-save-a-file-as-a-different-filename-but-keep-w-as-the-curr ...

  10. 控制反转IOC的依赖注入方式

    引言: 项目中遇到关于IOC的一些内容,因为和正常的逻辑代码比较起来,IOC有点反常.因此本文记录IOC的一些基础知识,并附有相应的简单实例,而在实际项目中再复杂的应用也只是在基本应用的基础上扩展而来 ...