设为首页 收藏本站
查看: 677|回复: 0

[经验分享] NSLock(Mac OS X (Cocoa) ) — Simple Explanation of What It Is and Why You Need It

[复制链接]

尚未签到

发表于 2015-12-30 15:59:44 | 显示全部楼层 |阅读模式
  http://alienryderflex.com/NSLock.html
  To understand why you need NSLock, study this little example:


int  x=0, y=0 ;   //  (globals used by all threads)

//  Spawn this method as a thread (via detachNewThreadSelector)
//  several times, to get multiple threads running.
- (void) renderPixels:(id) dummyObj {
  int  a, b ;
  while (YES) {
    //  Get the coordinates of a pixel that needs rendering, and
    //  advance x,y so another thread will pick up the next pixel.
    a=x; b=y; x++;
    if (x==640) {
      x=0; y++; }
    //  Terminate this thread if there are no more pixels to render.
    if (b>=480) return;
    //  Render one pixel.
    ...complex code to render the pixel a,b... }}
  
  This won’t work!  Why not?  Because the middle section (“Get the coordinates...”) will cause conflicts between threads.
  For example, what happens if one thread does x++, incrementing x from 639 to 640, and immediately after that another thread does a=x?  The value 640 would be stored in that thread’s a, which is never supposed to happen.
  Or, what if one thread does x++, incrementing x from 50 to 51, and immediately after that another thread also does x++, incrementing x from 51 to 52?  Both threads could render the same pixel (the one at x-position 50), and the pixel at x-position 51 would not be rendered at all.
  All kinds of horrible things can happen when multiple threads try to use the same global data — even though that data was intended to be used by multiple threads.  NSLock provides a nice way to prevent these problems:  Simply insert the yellow statements as shown here:


int  x=0, y=0 ;   //  (globals used by all threads)
NSLock  *theLock=[NSLock new] ;

//  Spawn this method as a thread (via detachNewThreadSelector)
//  several times, to get multiple threads running.
- (void) renderPixels:(id) dummyObj {
  int  a, b ;
  while (YES) {
    //  Get the coordinates of a pixel that needs rendering, and
    //  advance x,y so another thread will pick up the next pixel.
    [theLock lock];
    a=x; b=y; x++;
    if (x==640) {
      x=0; y++; }
    [theLock unlock];
    //  Terminate this thread if there are no more pixels to render.
    if (b>=480) return;
    //  Render one pixel.
    ...complex code to render the pixel a,b... }}
  
  Wow, can it really be that easy?  Yes it can.  Putting “lock” and “unlock” around the piece of code that deals with x and y protects it from conflicts.  Now, only one thread can be executing that piece of code at any given time.  If another thread wants to execute it too, it must wait until the first thread is finished (with the piece of protected code).
  That’s it — that’s all you really need to know to make great use of NSLock.  But if you’re curious about what those lock/unlock statements are really doing, read on:

· theLock has two states: locked or unlocked.
· If a thread tries to lock theLock, and it’s unlocked, then it becomes locked, and that thread continues executing normally.
· If a thread tries to lock theLock, but it’s already locked, then that thread is suspended, and that thread is added to some sort of list of threads that are waiting on this particular NSLock object.
· If a thread tries to unlock theLock, but it’s already unlocked, undesirable things may happen.  (If you used lock and unlock properly, as in the above example, this situation will never occur.)
· If a thread tries to unlock theLock, and it’s locked, and its list of waiting threads is empty, then theLock will become unlocked, and the thread that unlocked it will continue executing.
· If a thread tries to unlock theLock, and it’s locked, and its list of waiting threads is not empty, then theLock will stay locked, but one of the threads in its list (presumably the one that has been waiting longest) will be removed from the list and reactivated for normal execution.  (Also, the thread that just tried to unlock theLock will continue executing.)
    While a thread is suspended, waiting on an NSLock, it isn’t chewing up any processor time or occupying a processor core — it’s truly suspended, to be reactivated later by the NSLock object that it tried to lock.
  Question:  What if theLock is currently unlocked, and two threads try to lock it at the same time?  Won’t that create the same kind of conflict that we were trying to avoid in the first place?  Indeed, in theory it could.  There’s some seriously spooky magic going on in NSLock’s “lock” method, and my guess is that it involves a special, hardware-based conflict resolution technique.  Whatever it is, you probably wouldn’t want to deal with it in your own code, and it might even change substantially in future versions of the computer, so it’s best left to the OS to handle inside NSLock!
  (Update:  Since writing this page, I’ve found that conflict resolution can be performed with a special, atomic instruction that reads and writes a value “simultaneously” — i.e. without any possibility of another thread or processor accessing that value inbetween the read and the write.  Read more about it on the CocoaDev NSLock discussion.)
  Another question:  What if my thread method is performing just a single increment or decrement to a global, and that’s all it does?  (e.g. count++;)  I don’t need to put lock/unlock around that, do I?  If two processors try to increment count at the same time, it will just get incremented twice, with the memory chips moderating access, right?
  No, unfortunately, you can’t assume that.  What if your count++ gets compiled into processor-executable code that works something like this:

LOAD REG_A WITH [count]
INCR REG_A
STOR REG_A TO   [count]
  
  In that case, two processors could load the same value from count (say, 100), increment it (to 101), and store it back.  The value in count would be only 101, even though two threads tried to increment it with a starting value of 100.  So all code that changes global data must be protected.
  Yet another question:  What if part of my thread method reads some global data, but doesn’t write it?  That doesn’t need to be protected with lock/unlock, does it?  The answer is that the access doesn’t need to be protected if there is no possibility of the data being changed while the app is running in a multithreaded state.  Look at the original example where the protected code changed the values of x and y.  Notice that the code that reads the values of x and y (copying them to a and b) is also in the protected area.  That’s vital, because if you read the values of x and y in an unprotected section of code, another thread might be changing them at that time, so they might contain invalid values, or valid values that this thread shouldn’t be using.
  Think very carefully about whether global data access should be protected, and when in doubt, protect it!  Also be aware that all sections of code that read or write x and y must be protected by the sameNSLock object (theLock, in this case) — otherwise the protection is an illusion!

  Note:  If you’re using multithreading in your app, you’ll probably need to know about obtaining the processor count, so you will know the optimal number of threads to create.  For the above example, I would recommend creating the same number of pixel-rendering threads as you have processors.  So if you detect four processors, create four rendering threads.  That means your app will actually have five threads: the four rendering threads plus the main thread that handles user events, creation of rendering threads, etc.  That’s OK, because your main thread will usually be idle anyway.

Warning:  It’s a generally good idea to not lock two locks at a time in the same thread. Why not?
  Suppose you have two locks, A and B.  And suppose thread 1 locks A, while thread 2 locks B. Then, thread 1 tries to lock B, while thread 2 tries to lock A. What will happen? Thread 1 will go to sleep, to be woken up when B is unlocked. Thread 2 will go to sleep, to be woken up when A is unlocked. Both threads are suspended, and both locks are locked. Nothing further happens. Any other threads that try to lock A or B will also go to sleep and stay asleep. Your app is now hosed!

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.iyunv.com/thread-158561-1-1.html 上篇帖子: Apple Mac OS X每日一技巧009:option键和菜单栏的系统图标 下篇帖子: View Swapping(Chapter 29 of Cocoa Programming for Mac OS X)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表