zoukankan      html  css  js  c++  java
  • (转)谈谈多线程的思维方式

    前段时间仔细看过些关于多线程方面的资料,项目中用到线程的地方也不少,可是,当看了Jeffrey一篇关于锁的文章后,发现自己虽然一直都在使用多线程,但是缺少了做多线程编程需要的思维!所以想从Jeffrey的Optex(锁)入手,来谈谈我从其中体会的东西。

       NET中,我们用的最多的锁机制就是lock
    ,用起来很简单,短短几行程序就可以实现,例如:


    public class TestThreading
    {
        
    private System.Object lockThis = new System.Object();
        
    public void Function()
        {
            
    lock (lockThis)
            {
                
    // Access thread-sensitive resources.
            }
        }
    }

    其实我们也明白,lock并不是锁,而是MS提供的一个简便式的写法,真正实现的是Monitor类中的EnterExit方法,既然提到了Monitor类也就说下有个需要注意的地方:
        Pulse和PulseAll方法,这两个方法就是把锁状态将要改变的消息通知给等待队列中的线程,不过这时如果等待队列中没有线程,那么该方法就会一直等待下去,直到有等待的线程进入队列,也就是说该方法可能造成类试死锁的情况出现。

        上面的lock + 线程(Thread和ThreadPool) = 多线程编程(N%)!?
        对于该公式我曾经的N是80,现在是20。其中有很多东西影响我,让我从80->20,下面的Optex就是一个入口点。



    public sealed class Optex : IDisposable {
       
    private Int32 m_Waiters = 0;
       
    private Semaphore m_WaiterLock = new Semaphore(0, Int32.MaxValue);

       
    public Optex() { }
       
    public void Dispose() {
          
    if (m_WaiterLock != null)
          { 
             m_WaiterLock.Close(); 
             m_WaiterLock 
    = null;
          } 
       }

       
    public void Enter() {
          Thread.BeginCriticalRegion();
          
    // Add ourself to the set of threads interested in the Optex
          if (Interlocked.Increment(ref m_Waiters) == 1) {
             
    // If we were the first thread to show interest, we got it.
             return;
          }

          
    // Another thread has the Optex, we need to wait for it
          m_WaiterLock.WaitOne();
          
    // When WaitOne returns, this thread now has the Optex
       }

       
    public void Exit() {
          
    // Subtract ourself from the set of threads interested in the Optex
          if (Interlocked.Decrement(ref m_Waiters) > 0) {
             
    // Other threads are waiting, wake 1 of them
             m_WaiterLock.Release(1);
          }
          Thread.EndCriticalRegion();
       }
    }

       看完上面的代码,让我增加了两点认识: 
       1、Thread.BeginCriticalRegion()Thread.EndCriticalRegion();         

          
    因为这段时间正好看了一本多线程编程的书,既然将上面方法认为是进入临界区和退出临界区,对于临界区而言,进入该区的数据,在没有退出之前,如果临界区外的程序需要使用它,那么就必须出于等待。所以觉得已经使用临界区,为什么还要使用Semaphore?! 
       可是,MS只是取了个相同的名字,做的事情完全不同,上面两个方法完全没有临界区的概念,它只是设置一个区域(BeginEnd之间),表示该区域内发生线程中断或未处理的异常会影响整个应用程序域。

    2、m_Waiters的作用

            一开始以为在Enter的时候,直接写上:
                m_WaiterLock.WaitOne();

                  Exit的时候,写上: 
                       m_WaiterLock.Release(1);
                  
           
    这样就可以了。m_Waiters有什么意义?! 
               
       
    优化性能,Semaphore是内核对象,我们都知道,要尽量少的进入内核模式,因为这是很消耗性能,所以尽量少的使用内核对象。m_Waiters的意义就在这里,如果只有一个线程使用该锁对象的时候,是不需要去获取和释放的。 OK,上述的东西都是铺垫,铺完了也就进入主题了!

    多线程的思维



    namespace ThreadConcurrent.Lock
    {
        
    public sealed class Optex : IDisposable
        {
            
    /// <summary>
            
    /// 琐的状态
            
    /// </summary>
            private Int32 m_LockState = c_lsFree;

            
    /// <summary>
            
    /// 自由状态
            
    /// </summary>
            private const Int32 c_lsFree = 0x00000000;
            
            
    /// <summary>
            
    /// 被拥有状态
            
    /// </summary>
            private const Int32 c_lsOwned = 0x00000001;
            
            
    /// <summary>
            
    /// 等待的线程数
            
    /// </summary>
            private const Int32 c_1Waiter = 0x00000002;

            
    private Semaphore m_WaiterLock = new Semaphore(0, Int32.MaxValue);

            
    #region 构造函数

            
    /// <summary>
            
    /// 
            
    /// </summary>
            public Optex() { }

            
    #endregion

            
    /// <summary>
            
    /// 请求锁
            
    /// </summary>
            public void Enter()
            {
                Thread.BeginCriticalRegion();
                
    while (true)
                {
                    Int32 ls 
    = InterlockedOr(ref m_LockState, c_lsOwned);

                    
    //自由状态
                    if ((ls & c_lsOwned) == c_lsFree) return;

                    
    // 增加等待的线程数
                    if (IfThen(ref m_LockState, ls, ls + c_1Waiter))
                    {
                        m_WaiterLock.WaitOne();
                    }
                }
            }

            
    public void Exit()
            {
                
    // 释放琐
                Int32 ls = InterlockedAnd(ref m_LockState, ~c_lsOwned);

                
    //无等待的线程
                if (ls == c_lsOwned)
                {
                }
                
    else
                {
                    ls 
    &= ~c_lsOwned;
                    
    if (IfThen(ref m_LockState, ls & ~c_lsOwned, ls - c_1Waiter))
                    {
                        m_WaiterLock.Release(
    1);
                    }
                    
    else
                    {
                    }
                }
                Thread.EndCriticalRegion();
            }

            
    #region 原子化操作

            
    /// <summary>
            
    /// 与操作
            
    /// </summary>
            
    /// <param name="target"></param>
            
    /// <param name="with"></param>
            
    /// <returns></returns>
            private static Int32 InterlockedAnd(ref Int32 target, Int32 with)
            {
                Int32 i, j 
    = target;
                
    do
                {
                    i 
    = j;
                    j 
    = Interlocked.CompareExchange(ref target, i & with, i);
                } 
    while (i != j);
                
    return j;
            }

            
    /// <summary>
            
    /// 或操作
            
    /// </summary>
            
    /// <param name="target"></param>
            
    /// <param name="with"></param>
            
    /// <returns></returns>
            private static Int32 InterlockedOr(ref Int32 target, Int32 with)
            {
                Int32 i, j 
    = target;
                
    do
                {
                    i 
    = j;
                    j 
    = Interlocked.CompareExchange(ref target, i | with, i);
                } 
    while (i != j);
                
    return j;
            }

            
    #endregion

            
    private static Boolean IfThen(ref Int32 val, Int32 @if, Int32 then)
            {
                
    return (Interlocked.CompareExchange(ref val, @then, @if) == @if);
            }

            
    private static Boolean IfThen(ref Int32 val, Int32 @if, Int32 then, out Int32 prevVal)
            {
                prevVal 
    = Interlocked.CompareExchange(ref val, @then, @if);
                
    return (prevVal == @if);
            }

            
    /// <summary>
            
    /// 释放资源
            
    /// </summary>
            public void Dispose()
            {
                
    if (m_WaiterLock != null)
                {
                    m_WaiterLock.Close();
                    m_WaiterLock 
    = null;
                }
            }
        }
    }

         对于上面的这个代码,我晕眩了好一段时间,不过当我真正理解的时候,从晕眩中学到了做多线程编程应该具备的思维方式。
    首先从简单的理解开始谈,
    1、原子化操作
         对于InterLocked类,曾经也知道,但是却用的很少,不过从该代码中知道,在多线程的编程中对共享数据的写入操作,一定要达到原子性。至于如何做到这点,InterlockedAnd和InterlockedOr做了很好的诠释:
         While循环的目的就是保证target值以最新的值做与操作,如果传入的值在执行的过程被其他线程改变的话,那么是不会退出该循环的,并会利用改变后的值重新做次与操作。

    2、理解Enter和Exit
         这两个方法很难写出来解释,用图是最清晰的。
     

    曾经的晕眩:
    1、Enter方法中为什么存在循环,为什么不是执行完waitone就结束,必须m_lockState等于c_IsFree的时候才结束?
         线程的执行并不完全按照先前排好的顺序去执行,有时会发生一些特殊的情况来使改变线程的调度顺序,所以就可能会出现上图灰色部分的情况,则为了解决该可能发生的问题(概率很小)循环机制就出现了。
    2、为什么在WaitOne和Release之前,除了增加和减少等待者外,还需要判断m_lockstate是否改变(进入Enter到执行Waitone前的这段时间)?

    一般性的思维:
     

    该程序的思维:

          这样做的好处就是尽量少的操作内核对象,提高性能!

          多线程编程虽然复杂,但是我觉得很有意思和挑战性,而且随着硬件的发展,多线程编程会更加重要,既然已经上路就让我们走到尽头!

    http://www.cnblogs.com/Kevin-moon/archive/2009/04/24/1442469.html

  • 相关阅读:
    ADB命令大全
    Backup your Android without root or custom recovery -- adb backup
    Content portal for Pocketables Tasker articles
    Is there a way to detect if call is in progress? Phone Event
    Tasker to proximity screen off
    Tasker to detect application running in background
    Tasker to create toggle widget for ES ftp service -- Send Intent
    Tasker to proximity screen on
    Tasker to answer incoming call by pressing power button
    Tasker to stop Poweramp control for the headset while there is an incoming SMS
  • 原文地址:https://www.cnblogs.com/blsong/p/2183840.html
Copyright © 2011-2022 走看看