private void Work()
        {
            Viewport viewport_prev = new Viewport();
            Viewport viewport_curr = new Viewport();
            bool     drew          = false;

            while (true)
            {
                long   beg = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); // 当前帧开始的时间
                double dX = 0, dY = 0;

                // 获取画面的几何信息, 保存thread local
                var pendingActions = msgq.CheckOut();
                if (pendingActions.Length == 0)
                {
                    long end   = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
                    long delta = end - beg;
                    int  ms    = (int)(period - delta);
                    Thread.Sleep(ms);
                    continue; // 注意不是执行剩余的循环体, 而是开始新的循环.
                }

                // 「打草稿」阶段
                foreach (var act in pendingActions)
                {
                    if (act is ViewportMessage)
                    {
                        if (act is MoveMessage)
                        {
                            var a = act as MoveMessage;
                            dX += a.dXScreen;
                            dY += a.dYScreen;
                        }
                        else if (act is ZoomMessage)
                        {
                            var a = act as ZoomMessage;
                            viewport_curr.Zoom(a.nScroll, a.XScreen, a.YScreen);
                        }
                        else if (act is ResizeMessage)
                        {
                            var a = act as ResizeMessage;
                            viewport_curr.Resize(a.WScreen, a.HScreen);
                        }
                        else if (act is ThumbJumpMessage)
                        {
                            var a = act as ThumbJumpMessage;
                            viewport_curr.X = a.CenterXScreen * thumbDensity - viewport_curr.ToActualPixel(viewport_curr.OutW) / 2;
                            viewport_curr.Y = a.CenterYScreen * thumbDensity - viewport_curr.ToActualPixel(viewport_curr.OutH) / 2;
                        }
                    }
                    else if (act is PolylineMessage)
                    {
                        drew = true;
                        if (act is AddVertexMessage)
                        {
                            var    a = act as AddVertexMessage;
                            double x = viewport_curr.X + viewport_curr.ToActualPixel(a.x);
                            double y = viewport_curr.Y + viewport_curr.ToActualPixel(a.y);
                            poly.AddVertex(a.idv, x, y, a.prev);
                        }
                        else if (act is ConnectVertexMessage)
                        {
                            var a = act as ConnectVertexMessage;
                            poly.ConnectVertex(a.idv1, a.idv2);
                        }
                        else if (act is MoveVertexMessgae)
                        {
                            var    a  = act as MoveVertexMessgae;
                            double dx = viewport_curr.ToActualPixel(a.dx);
                            double dy = viewport_curr.ToActualPixel(a.dy);
                            poly.MoveVertex(a.idv, dx, dy);
                        }
                        else if (act is DeleteVertexMessage)
                        {
                            var a = act as DeleteVertexMessage;
                            poly.DeleteVertex(a.idv);
                        }
                    }
                    else if (act is ComputeMessage)
                    {
                        if (act is Ki67Message)
                        {
                            var a = act as Ki67Message;
                            if (!quantTasks.ContainsKey(a.idv))
                            {
                                poly.QueryChain(a.idv, out List <double> x, out List <double> y, out BoundingBox bb);
                                byte[] data = slide.LoadRegionBMP(bb);

                                //using (var fs = new FileStream(@"C:\Users\winston\Pictures\aaa.bmp", FileMode.Create))
                                //{
                                //    fs.Write(data, 0, data.Length);
                                //}

                                var task = new Ki67Task(a.idv, data, x, y);

                                quantTasks.Add(a.idv, task);
                                task.Start();
                            }
                        }
                    }
                    else if (act is FileMessage)
                    {
                        if (act is LoadSlideMessage)
                        {
                            var a = act as LoadSlideMessage;
                            slide.Open(a.Path, viewport_curr);
                            viewport_curr.OutW = a.WCanvas;
                            viewport_curr.OutH = a.HCanvas;
                            canvasThumb.Dispatcher.Invoke(() =>
                            {
                                using (var ms = new System.IO.MemoryStream(slide.LoadThumbJPG()))
                                {
                                    var bi = new BitmapImage();
                                    bi.BeginInit();
                                    bi.CacheOption  = BitmapCacheOption.OnLoad;
                                    bi.StreamSource = ms;
                                    bi.EndInit();
                                    UniformToFill(viewport_curr.SlideW, viewport_curr.SlideH,
                                                  out double thumbW, out double thumbH);
                                    (canvasThumb.Width, canvasThumb.Height) = (thumbW, thumbH);
                                    canvasThumb.UpdateLayout();
                                    imageThumb.ImageSource = bi;
                                    //Console.WriteLine($"actualWH=({canvasThumb.ActualWidth},{canvasThumb.ActualHeight})");
                                }
                            }, System.Windows.Threading.DispatcherPriority.Send);
                        }
                        else if (act is CloseSlideMessage)
                        {
                            //poly.Close();
                            slide.Close();
                        }
                    }
                }

                viewport_curr.Move(dX, dY);

                #region 摆放前端对象
                byte[]        img   = slide.LoadRegionBMP(viewport_curr);
                List <Object> items = poly.LoadRegionShapes(viewport_curr);
                canvasMain.Dispatcher.Invoke(() =>
                {
                    #region 重新绘制矢量物体
                    canvasMain.Children.Clear();
                    foreach (var item in items)
                    {
                        if (item is V)
                        {
                            var v = item as V;
                            DrawVertex(v, viewport_curr);
                        }
                        else if (item is E)
                        {
                            var e = item as E;
                            DrawEdge(e, viewport_curr);
                        }
                    }
                    canvasMain.UpdateLayout();
                    #endregion

                    #region 重新绘制位图
                    BitmapImage bi = null;
                    using (var ms = new System.IO.MemoryStream(img))
                    {
                        bi = new BitmapImage();
                        bi.BeginInit();
                        bi.CacheOption  = BitmapCacheOption.OnLoad;
                        bi.StreamSource = ms;
                        bi.EndInit();
                    }
                    imageMain.ImageSource = bi;
                    #endregion
                }, System.Windows.Threading.DispatcherPriority.Send);
                canvasThumb.Dispatcher.Invoke(() =>
                {
                    canvasThumb.Children.Clear();
                    DrawOnThumb(canvasThumb, viewport_curr);
                }, System.Windows.Threading.DispatcherPriority.Send);
                viewport_prev.CopyFrom(viewport_curr);

                var scores    = new Dictionary <int, double>();
                var to_remove = new List <int>();
                foreach (var _ in quantTasks)
                {
                    var t = _.Value;
                    if (!double.IsNaN(t.Result))
                    {
                        scores.Add(t.HeadId, t.Result);
                        to_remove.Add(t.HeadId);
                    }
                }

                foreach (var _ in to_remove)
                {
                    quantTasks.Remove(_);
                }

                scoreBoard.Dispatcher.Invoke(() =>
                {
                    foreach (var _ in scores)
                    {
                        scoreBoard.Text = $"PolygonId: {_.Key}. Ki67={(int)(_.Value * 100)}%\r\n";
                    }
                }, System.Windows.Threading.DispatcherPriority.Send);

                // 如果end-beg小于period, 则忙等到period
                for (long end = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); // 当前帧结束的时间
                     end - beg < period;
                     end = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds())
                {
                    Thread.Sleep(1); // 降低cpu占用率. 改善阅览体验.
                }
                #endregion
            }
        }