Beispiel #1
0
        internal static void Warmup()
        {
            // Bench 128 4 17 yields:

            // CheckInfoBroker: 140 (4/32/40/32/32)
            // EvalInfoBroker: 16 (4/4/4/4)
            // SwapListBroker: 16 (4/4/4/4)
            // MovesSearchedBroker: 120 (28/36/28/28)
            // PositionBroker: 32 (8/8/8/8)
            // StateInfoArrayBroker: 16 (4/4/4/4)

            // MListBroker: 20 (4/4/4/4/4)
            // LoopStackBroker: 32 (8/8/8/8)
            // MovePickerBroker: 136 (32/40/32/32)
            // StateInfoBroker: 132 (32/36/32/32)

            // Specific allocation not to overallocate memory for nothing
            int i, brokerSize;

            // Reusing brokers
            brokerSize = 40; for (i = 0; i < brokerSize; i++) { CheckInfoBroker.GetObject(); } for (i = 0; i < brokerSize; i++) { CheckInfoBroker.Free(); }
            brokerSize = 4; for (i = 0; i < brokerSize; i++) { EvalInfoBroker.GetObject(); } for (i = 0; i < brokerSize; i++) { EvalInfoBroker.Free(); }
            brokerSize = 4; for (i = 0; i < brokerSize; i++) { SwapListBroker.GetObject(); } for (i = 0; i < brokerSize; i++) { SwapListBroker.Free(); }
            brokerSize = 36; for (i = 0; i < brokerSize; i++) { MovesSearchedBroker.GetObject(); } for (i = 0; i < brokerSize; i++) { MovesSearchedBroker.Free(); }
            brokerSize = 8; for (i = 0; i < brokerSize; i++) { PositionBroker.GetObject(); } for (i = 0; i < brokerSize; i++) { PositionBroker.Free(); }
            brokerSize = 4; for (i = 0; i < brokerSize; i++) { StateInfoArrayBroker.GetObject(); } for (i = 0; i < brokerSize; i++) { StateInfoArrayBroker.Free(); }
            brokerSize = 4; for (i = 0; i < brokerSize; i++) { MListBroker.GetObject(); } for (i = 0; i < brokerSize; i++) { MListBroker.Free(); }
            brokerSize = 36; for (i = 0; i < brokerSize; i++) { StateInfoBroker.GetObject(); } for (i = 0; i < brokerSize; i++) { StateInfoBroker.Free(); }

            // Recycling brokers
            brokerSize = 8; LoopStack[] arrLoopStack = new LoopStack[brokerSize]; for (i = 0; i < brokerSize; i++) { arrLoopStack[i] = LoopStackBroker.GetObject(); } for (i = brokerSize - 1; i >= 0; i--) { LoopStackBroker.Free(arrLoopStack[i]); }
            brokerSize = 40; MovePicker[] arrMovePicker = new MovePicker[brokerSize]; for (i = 0; i < brokerSize; i++) { arrMovePicker[i] = MovePickerBroker.GetObject(); } for (i = brokerSize - 1; i >= 0; i--) { MovePickerBroker.Free(arrMovePicker[i]); }
        }
Beispiel #2
0
 internal static void init()
 {
     for (int i = 0; i < Constants.BROKER_SLOTS; i++)
     {
         _pool[i] = new MovePicker[0];
     }
 }
Beispiel #3
0
        internal static void Free(MovePicker obj)
        {
            obj.Recycle();
#if WINDOWS_RT
            _cnt[Environment.CurrentManagedThreadId & Constants.BROKER_SLOT_MASK]--;
#else
            _cnt[System.Threading.Thread.CurrentThread.ManagedThreadId & Constants.BROKER_SLOT_MASK]--;
#endif
        }
Beispiel #4
0
        internal static MovePicker GetObject()
        {
            int slotID = System.Threading.Thread.CurrentThread.ManagedThreadId & Constants.BROKER_SLOT_MASK;

            if (_cnt[slotID] == _pool[slotID].Length)
            {
                int          poolLength = _pool[slotID].Length;
                MovePicker[] temp       = new MovePicker[poolLength + Constants.BrokerCapacity];
                Array.Copy(_pool[slotID], temp, poolLength);
                for (int i = 0; i < Constants.BrokerCapacity; i++)
                {
                    temp[poolLength + i] = new MovePicker();
                }
                _pool[slotID] = temp;
            }
            return(_pool[slotID][_cnt[slotID]++]);
        }
Beispiel #5
0
        /// Constructors of the MovePicker class. As arguments we pass information
        /// to help it to return the presumably good moves first, to decide which
        /// moves to return (in the quiescence search, for instance, we only want to
        /// search captures, promotions and some checks) and about how important good
        /// move ordering is at the current node.
        internal void MovePickerC(Position p, Move ttm, Depth d, History h,
                                  Stack ss, Value beta, MovePicker mpExt)
        {
            pos        = p;
            H          = h;
            depth      = d;
            mpExternal = mpExt;

            Debug.Assert(d > DepthC.DEPTH_ZERO);

            captureThreshold  = 0;
            curMovePos        = lastMovePos = 0;
            lastBadCapturePos = Constants.MAX_MOVES - 1;

            recaptureSquare = 0;
            lastQuietPos    = 0;
            mpos            = 0;

            if (p.in_check())
            {
                phase = SequencerC.EVASION;
            }
            else
            {
                phase = SequencerC.MAIN_SEARCH;

                ms[Constants.MAX_MOVES].move     = ss.killers0;
                ms[Constants.MAX_MOVES + 1].move = ss.killers1;

                // Consider sligtly negative captures as good if at low depth and far from beta
                if (ss.eval < beta - Constants.PawnValueMidgame && d < 3 * DepthC.ONE_PLY)
                {
                    captureThreshold = -Constants.PawnValueMidgame;
                }

                // Consider negative captures as good if still enough to reach beta
                else if (ss.eval > beta)
                {
                    captureThreshold = beta - ss.eval;
                }
            }

            ttMove       = (ttm != 0 && pos.is_pseudo_legal(ttm) ? ttm : MoveC.MOVE_NONE);
            lastMovePos += ((ttMove != MoveC.MOVE_NONE) ? 1 : 0);
        }
Beispiel #6
0
 internal static void init()
 {
     for (int i = 0; i < Constants.BROKER_SLOTS; i++)
     {
         _pool[i] = new MovePicker[0];
     }
 }
Beispiel #7
0
 internal static void Free(MovePicker obj)
 {
     obj.Recycle();
     _cnt[System.Threading.Thread.CurrentThread.ManagedThreadId & Constants.BROKER_SLOT_MASK]--;
 }
Beispiel #8
0
 internal static MovePicker GetObject()
 {
     int slotID = System.Threading.Thread.CurrentThread.ManagedThreadId & Constants.BROKER_SLOT_MASK;
     if (_cnt[slotID] == _pool[slotID].Length)
     {
         int poolLength = _pool[slotID].Length;
         MovePicker[] temp = new MovePicker[poolLength + Constants.BrokerCapacity];
         Array.Copy(_pool[slotID], temp, poolLength);
         for (int i = 0; i < Constants.BrokerCapacity; i++)
         {
             temp[poolLength + i] = new MovePicker();
         }
         _pool[slotID] = temp;
     }
     return _pool[slotID][_cnt[slotID]++];
 }
Beispiel #9
0
        // split() does the actual work of distributing the work at a node between
        // several available threads. If it does not succeed in splitting the node
        // (because no idle threads are available, or because we have no unused split
        // point objects), the function immediately returns. If splitting is possible, a
        // SplitPoint object is initialized with all the data that must be copied to the
        // helper threads and then helper threads are told that they have been assigned
        // work. This will cause them to instantly leave their idle loops and call
        // search(). When all threads have returned from search() then split() returns.
        internal static Value split(bool Fake, Position pos, Stack[] ss, int ssPos, Value alpha, Value beta,
                                    Value bestValue, ref Move bestMove, Depth depth, Move threatMove,
                                    int moveCount, MovePicker mp, int nodeType)
        {
            Debug.Assert(pos.pos_is_ok());
            Debug.Assert(bestValue > -ValueC.VALUE_INFINITE);
            Debug.Assert(bestValue <= alpha);
            Debug.Assert(alpha < beta);
            Debug.Assert(beta <= ValueC.VALUE_INFINITE);
            Debug.Assert(depth > DepthC.DEPTH_ZERO);

            Thread master = pos.this_thread();

            if (master.splitPointsCnt >= Constants.MAX_SPLITPOINTS_PER_THREAD)
            {
                return(bestValue);
            }

            // Pick the next available split point from the split point stack
            SplitPoint sp = master.splitPoints[master.splitPointsCnt];

            sp.parent     = master.curSplitPoint;
            sp.master     = master;
            sp.cutoff     = false;
            sp.slavesMask = 1UL << master.idx;
#if ACTIVE_REPARENT
            sp.allSlavesRunning = true;
#endif

            sp.depth      = depth;
            sp.bestMove   = bestMove;
            sp.threatMove = threatMove;
            sp.alpha      = alpha;
            sp.beta       = beta;
            sp.nodeType   = nodeType;
            sp.bestValue  = bestValue;
            sp.mp         = mp;
            sp.moveCount  = moveCount;
            sp.pos        = pos;
            sp.nodes      = 0;
            sp.ss         = ss;
            sp.ssPos      = ssPos;

            Debug.Assert(master.is_searching);
            master.curSplitPoint = sp;

            int slavesCnt = 0;

            ThreadHelper.lock_grab(sp.Lock);
            ThreadHelper.lock_grab(splitLock);

            for (int i = 0; i < size() && !Fake; ++i)
            {
                if (threads[i].is_available_to(master))
                {
                    sp.slavesMask           |= 1UL << i;
                    threads[i].curSplitPoint = sp;
                    threads[i].is_searching  = true; // Slave leaves idle_loop()

                    if (useSleepingThreads)
                    {
                        threads[i].wake_up();
                    }

                    if (++slavesCnt + 1 >= maxThreadsPerSplitPoint) // Master is always included
                    {
                        break;
                    }
                }
            }

            master.splitPointsCnt++;

            ThreadHelper.lock_release(splitLock);
            ThreadHelper.lock_release(sp.Lock);

            // Everything is set up. The master thread enters the idle loop, from which
            // it will instantly launch a search, because its is_searching flag is set.
            // We pass the split point as a parameter to the idle loop, which means that
            // the thread will return from the idle loop when all slaves have finished
            // their work at this split point.
            if (slavesCnt != 0 || Fake)
            {
                master.idle_loop(sp, null);
                // In helpful master concept a master can help only a sub-tree of its split
                // point, and because here is all finished is not possible master is booked.
                Debug.Assert(!master.is_searching);
            }

            // We have returned from the idle loop, which means that all threads are
            // finished. Note that setting is_searching and decreasing activeSplitPoints is
            // done under lock protection to avoid a race with Thread::is_available_to().
            ThreadHelper.lock_grab(sp.Lock); // To protect sp->nodes
            ThreadHelper.lock_grab(splitLock);

            master.is_searching = true;
            master.splitPointsCnt--;
            master.curSplitPoint = sp.parent;
            pos.nodes           += sp.nodes;
            bestMove             = sp.bestMove;

            ThreadHelper.lock_release(splitLock);
            ThreadHelper.lock_release(sp.Lock);

            return(sp.bestValue);
        }
Beispiel #10
0
 internal static void Free(MovePicker obj)
 {
     obj.Recycle();
     _cnt[System.Threading.Thread.CurrentThread.ManagedThreadId & Constants.BROKER_SLOT_MASK]--;
 }
Beispiel #11
0
 public void Recycle()
 {
     pos = null;
     H = null;
     mpExternal = null;
 }
Beispiel #12
0
        // split() does the actual work of distributing the work at a node between
        // several available threads. If it does not succeed in splitting the node
        // (because no idle threads are available, or because we have no unused split
        // point objects), the function immediately returns. If splitting is possible, a
        // SplitPoint object is initialized with all the data that must be copied to the
        // helper threads and then helper threads are told that they have been assigned
        // work. This will cause them to instantly leave their idle loops and call
        // search(). When all threads have returned from search() then split() returns.
        internal static Value split(bool Fake, Position pos, Stack[] ss, int ssPos, Value alpha, Value beta,
                                    Value bestValue, ref Move bestMove, Depth depth, Move threatMove,
                                    int moveCount, MovePicker mp, int nodeType)
        {
            Debug.Assert(pos.pos_is_ok());
            Debug.Assert(bestValue > -ValueC.VALUE_INFINITE);
            Debug.Assert(bestValue <= alpha);
            Debug.Assert(alpha < beta);
            Debug.Assert(beta <= ValueC.VALUE_INFINITE);
            Debug.Assert(depth > DepthC.DEPTH_ZERO);

            Thread master = pos.this_thread();

            if (master.splitPointsCnt >= Constants.MAX_SPLITPOINTS_PER_THREAD)
                return bestValue;

            // Pick the next available split point from the split point stack
            SplitPoint sp = master.splitPoints[master.splitPointsCnt];

            sp.parent = master.curSplitPoint;
            sp.master = master;
            sp.cutoff = false;
            sp.slavesMask = 1UL << master.idx;
#if ACTIVE_REPARENT
            sp.allSlavesRunning = true;
#endif

            sp.depth = depth;
            sp.bestMove = bestMove;
            sp.threatMove = threatMove;
            sp.alpha = alpha;
            sp.beta = beta;
            sp.nodeType = nodeType;
            sp.bestValue = bestValue;
            sp.mp = mp;
            sp.moveCount = moveCount;
            sp.pos = pos;
            sp.nodes = 0;
            sp.ss = ss;
            sp.ssPos = ssPos;

            Debug.Assert(master.is_searching);
            master.curSplitPoint = sp;

            int slavesCnt = 0;

            ThreadHelper.lock_grab(sp.Lock);
            ThreadHelper.lock_grab(splitLock);

            for (int i = 0; i < size() && !Fake; ++i)
                if (threads[i].is_available_to(master))
                {
                    sp.slavesMask |= 1UL << i;
                    threads[i].curSplitPoint = sp;
                    threads[i].is_searching = true; // Slave leaves idle_loop()

                    if (useSleepingThreads)
                        threads[i].wake_up();

                    if (++slavesCnt + 1 >= maxThreadsPerSplitPoint) // Master is always included
                        break;
                }

            master.splitPointsCnt++;

            ThreadHelper.lock_release(splitLock);
            ThreadHelper.lock_release(sp.Lock);

            // Everything is set up. The master thread enters the idle loop, from which
            // it will instantly launch a search, because its is_searching flag is set.
            // We pass the split point as a parameter to the idle loop, which means that
            // the thread will return from the idle loop when all slaves have finished
            // their work at this split point.
            if (slavesCnt != 0 || Fake)
            {
                master.idle_loop(sp, null);
                // In helpful master concept a master can help only a sub-tree of its split
                // point, and because here is all finished is not possible master is booked.
                Debug.Assert(!master.is_searching);
            }

            // We have returned from the idle loop, which means that all threads are
            // finished. Note that setting is_searching and decreasing activeSplitPoints is
            // done under lock protection to avoid a race with Thread::is_available_to().
            ThreadHelper.lock_grab(sp.Lock); // To protect sp->nodes
            ThreadHelper.lock_grab(splitLock);

            master.is_searching = true;
            master.splitPointsCnt--;
            master.curSplitPoint = sp.parent;
            pos.nodes += sp.nodes;
            bestMove = sp.bestMove;

            ThreadHelper.lock_release(splitLock);
            ThreadHelper.lock_release(sp.Lock);

            return sp.bestValue;
        }
Beispiel #13
0
        /// Constructors of the MovePicker class. As arguments we pass information
        /// to help it to return the presumably good moves first, to decide which
        /// moves to return (in the quiescence search, for instance, we only want to
        /// search captures, promotions and some checks) and about how important good
        /// move ordering is at the current node.
        internal void MovePickerC(Position p, Move ttm, Depth d, History h,
                       Stack ss, Value beta, MovePicker mpExt)
        {
            pos = p;
            H = h;
            depth = d;
            mpExternal = mpExt;

            Debug.Assert(d > DepthC.DEPTH_ZERO);

            captureThreshold = 0;
            curMovePos = lastMovePos = 0;
            lastBadCapturePos = Constants.MAX_MOVES - 1;

            recaptureSquare = 0;
            lastQuietPos = 0;
            mpos = 0;

            if (p.in_check())
            {
                phase = SequencerC.EVASION;
            }
            else
            {
                phase = SequencerC.MAIN_SEARCH;

                ms[Constants.MAX_MOVES].move = ss.killers0;
                ms[Constants.MAX_MOVES + 1].move = ss.killers1;

                // Consider sligtly negative captures as good if at low depth and far from beta
                if (ss.eval < beta - Constants.PawnValueMidgame && d < 3 * DepthC.ONE_PLY)
                    captureThreshold = -Constants.PawnValueMidgame;

                // Consider negative captures as good if still enough to reach beta
                else if (ss.eval > beta)
                    captureThreshold = beta - ss.eval;
            }

            ttMove = (ttm != 0 && pos.is_pseudo_legal(ttm) ? ttm : MoveC.MOVE_NONE);
            lastMovePos += ((ttMove != MoveC.MOVE_NONE) ? 1 : 0);
        }
Beispiel #14
0
 public void Recycle()
 {
     pos        = null;
     H          = null;
     mpExternal = null;
 }
Beispiel #15
0
 public void Recycle()
 {
     this.pos = null;
     this.H = null;
     this.mpExternal = null;
 }
Beispiel #16
0
        internal static void Warmup()
        {
            // Bench 128 4 17 yields:

            // CheckInfoBroker: 140 (4/32/40/32/32)
            // EvalInfoBroker: 16 (4/4/4/4)
            // SwapListBroker: 16 (4/4/4/4)
            // MovesSearchedBroker: 120 (28/36/28/28)
            // PositionBroker: 32 (8/8/8/8)
            // StateInfoArrayBroker: 16 (4/4/4/4)

            // MListBroker: 20 (4/4/4/4/4)
            // LoopStackBroker: 32 (8/8/8/8)
            // MovePickerBroker: 136 (32/40/32/32)
            // StateInfoBroker: 132 (32/36/32/32)

            // Specific allocation not to overallocate memory for nothing
            int i, brokerSize;

            // Reusing brokers
            brokerSize = 40; for (i = 0; i < brokerSize; i++)
            {
                CheckInfoBroker.GetObject();
            }
            for (i = 0; i < brokerSize; i++)
            {
                CheckInfoBroker.Free();
            }
            brokerSize = 4; for (i = 0; i < brokerSize; i++)
            {
                EvalInfoBroker.GetObject();
            }
            for (i = 0; i < brokerSize; i++)
            {
                EvalInfoBroker.Free();
            }
            brokerSize = 4; for (i = 0; i < brokerSize; i++)
            {
                SwapListBroker.GetObject();
            }
            for (i = 0; i < brokerSize; i++)
            {
                SwapListBroker.Free();
            }
            brokerSize = 36; for (i = 0; i < brokerSize; i++)
            {
                MovesSearchedBroker.GetObject();
            }
            for (i = 0; i < brokerSize; i++)
            {
                MovesSearchedBroker.Free();
            }
            brokerSize = 8; for (i = 0; i < brokerSize; i++)
            {
                PositionBroker.GetObject();
            }
            for (i = 0; i < brokerSize; i++)
            {
                PositionBroker.Free();
            }
            brokerSize = 4; for (i = 0; i < brokerSize; i++)
            {
                StateInfoArrayBroker.GetObject();
            }
            for (i = 0; i < brokerSize; i++)
            {
                StateInfoArrayBroker.Free();
            }
            brokerSize = 4; for (i = 0; i < brokerSize; i++)
            {
                MListBroker.GetObject();
            }
            for (i = 0; i < brokerSize; i++)
            {
                MListBroker.Free();
            }
            brokerSize = 36; for (i = 0; i < brokerSize; i++)
            {
                StateInfoBroker.GetObject();
            }
            for (i = 0; i < brokerSize; i++)
            {
                StateInfoBroker.Free();
            }

            // Recycling brokers
            brokerSize = 8; LoopStack[] arrLoopStack = new LoopStack[brokerSize]; for (i = 0; i < brokerSize; i++)
            {
                arrLoopStack[i] = LoopStackBroker.GetObject();
            }
            for (i = brokerSize - 1; i >= 0; i--)
            {
                LoopStackBroker.Free(arrLoopStack[i]);
            }
            brokerSize = 40; MovePicker[] arrMovePicker = new MovePicker[brokerSize]; for (i = 0; i < brokerSize; i++)
            {
                arrMovePicker[i] = MovePickerBroker.GetObject();
            }
            for (i = brokerSize - 1; i >= 0; i--)
            {
                MovePickerBroker.Free(arrMovePicker[i]);
            }
        }
Beispiel #17
0
        // split() does the actual work of distributing the work at a node between
        // several available threads. If it does not succeed in splitting the node
        // (because no idle threads are available), the function immediately returns.
        // If splitting is possible, a SplitPoint object is initialized with all the
        // data that must be copied to the helper threads and then helper threads are
        // told that they have been assigned work. This will cause them to instantly
        // leave their idle loops and call search(). When all threads have returned from
        // search() then split() returns.
        internal static void split(
            bool Fake,
            Position pos,
            Stack[] ss,
            int ssPos,
            int alpha,
            int beta,
            ref int bestValue,
            ref int bestMove,
            int depth,
            int threatMove,
            int moveCount,
            MovePicker movePicker,
            int nodeType)
        {
            Debug.Assert(bestValue <= alpha && alpha < beta && beta <= ValueC.VALUE_INFINITE);
            Debug.Assert(pos.pos_is_ok());
            Debug.Assert(bestValue > -ValueC.VALUE_INFINITE);
            Debug.Assert(depth > DepthC.DEPTH_ZERO);

            var thisThread = pos.this_thread();

            Debug.Assert(thisThread.searching);
            Debug.Assert(thisThread.splitPointsSize < Constants.MAX_SPLITPOINTS_PER_THREAD);

            // Pick the next available split point from the split point stack
            var sp = thisThread.splitPoints[thisThread.splitPointsSize];

            sp.parentSplitPoint = thisThread.activeSplitPoint;
            sp.master = thisThread;
            sp.cutoff = false;
            sp.slavesMask = 1UL << thisThread.idx;
#if ACTIVE_REPARENT
            sp.allSlavesRunning = true;
#endif

            sp.depth = depth;
            sp.bestMove = bestMove;
            sp.threatMove = threatMove;
            sp.alpha = alpha;
            sp.beta = beta;
            sp.nodeType = nodeType;
            sp.bestValue = bestValue;
            sp.movePicker = movePicker;
            sp.moveCount = moveCount;
            sp.pos = pos;
            sp.nodes = 0;
            sp.ss = ss;
            sp.ssPos = ssPos;

            // Try to allocate available threads and ask them to start searching setting
            // 'searching' flag. This must be done under lock protection to avoid concurrent
            // allocation of the same slave by another master.
            ThreadHelper.lock_grab(splitLock);
            ThreadHelper.lock_grab(sp.Lock);

            thisThread.splitPointsSize++;
            thisThread.activeSplitPoint = sp;
            thisThread.activePosition = null;

            var slavesCnt = 1; // Master is always included
            Thread slave;
            while ((slave = Threads.available_slave(thisThread)) != null
                && ++slavesCnt <= Threads.maxThreadsPerSplitPoint && !Fake)
            {
                sp.slavesMask |= 1UL << slave.idx;
                slave.activeSplitPoint = sp;
                slave.searching = true; // Slave leaves idle_loop()

                slave.notify_one(); // Could be sleeping
            }

            ThreadHelper.lock_release(sp.Lock);
            ThreadHelper.lock_release(splitLock);

            // Everything is set up. The master thread enters the idle loop, from which
            // it will instantly launch a search, because its searching flag is set.
            // We pass the split point as a parameter to the idle loop, which means that
            // the thread will return from the idle loop when all slaves have finished
            // their work at this split point.
            if (slavesCnt > 1 || Fake)
            {
                thisThread.base_idle_loop(null); // Force a call to base class idle_loop()

                // In helpful master concept a master can help only a sub-tree of its split
                // point, and because here is all finished is not possible master is booked.
                Debug.Assert(!thisThread.searching);
                Debug.Assert(thisThread.activePosition == null);
            }

            // We have returned from the idle loop, which means that all threads are
            // finished. Note that setting searching and decreasing activeSplitPoints is
            // done under lock protection to avoid a race with Thread::is_available_to().
            ThreadHelper.lock_grab(splitLock);
            ThreadHelper.lock_grab(sp.Lock); // To protect sp->nodes

            thisThread.searching = true;
            thisThread.splitPointsSize--;
            thisThread.activeSplitPoint = sp.parentSplitPoint;
            thisThread.activePosition = pos;
            pos.nodes += sp.nodes;
            bestMove = sp.bestMove;
            bestValue = sp.bestValue;

            ThreadHelper.lock_release(sp.Lock);
            ThreadHelper.lock_release(splitLock);
        }