Multithreading 如何在动画完成之前阻止对方法的访问

Multithreading 如何在动画完成之前阻止对方法的访问,multithreading,silverlight,Multithreading,Silverlight,我有一个Silverlight应用程序。它有一个基本的动画,其中一个矩形被动画设置到一个新的位置。该动画由两个双动画组成-一个变换X,另一个变换Y。效果正常 我基本上想阻止对这个动画方法的任何其他调用,直到前两个动画完成。我看到DoubleAnimation类有一个它触发的已完成事件,但我没有成功地构造任何类型的代码,直到这两个类都完成了才能成功地阻止它们 我尝试使用Monitor.Enter在输入方法时启用私有成员,然后从其中一个动画完成事件中释放锁,但我尝试链接这两个事件,以便在两个事件都完

我有一个Silverlight应用程序。它有一个基本的动画,其中一个矩形被动画设置到一个新的位置。该动画由两个双动画组成-一个变换X,另一个变换Y。效果正常

我基本上想阻止对这个动画方法的任何其他调用,直到前两个动画完成。我看到DoubleAnimation类有一个它触发的已完成事件,但我没有成功地构造任何类型的代码,直到这两个类都完成了才能成功地阻止它们

我尝试使用Monitor.Enter在输入方法时启用私有成员,然后从其中一个动画完成事件中释放锁,但我尝试链接这两个事件,以便在两个事件都完成之前不会释放锁,但都没有成功

以下是动画方法的外观:

    public void AnimateRectangle(Rectangle rect, double newX, double newY)
    {
            var xIsComplete = false;

            Duration duration = new Duration(new TimeSpan(0, 0, 0, 1, 350));
            var easing = new ElasticEase() { EasingMode = EasingMode.EaseOut, Oscillations = 1, Springiness = 4 };
            var animateX = new DoubleAnimation();
            var animateY = new DoubleAnimation();

            animateX.EasingFunction = easing;
            animateX.Duration = duration;
            animateY.EasingFunction = easing;
            animateY.Duration = duration;

            var sb = new Storyboard();

            sb.Duration = duration;
            sb.Children.Add(animateX);
            sb.Children.Add(animateY);

            Storyboard.SetTarget(animateX, rect);
            Storyboard.SetTargetProperty(animateX, new PropertyPath("(Canvas.Left)"));
            Storyboard.SetTarget(animateY, rect);
            Storyboard.SetTargetProperty(animateY, new PropertyPath("(Canvas.Top)"));

            animateX.To = newX;
            animateY.To = newY;
            sb.Begin();

    }
编辑添加的更多信息

我一开始遇到这个问题是因为我从另一个方法调用这个方法,因为它处理了它调用动画的项目。我注意到这些东西并没有达到我预期的效果。我传入的新X/Y坐标基于项目当前位置,因此,如果在完成之前多次调用它,那么它最终将位于错误的位置。作为测试,我添加了一个只运行一次动画的按钮。成功了。但是,如果我连续多次单击按钮,我会看到与以前相同的行为:项目最终位于错误的位置

是的,Silverlight动画似乎在主UI线程上运行。在我尝试的一个测试中,我添加了两个属性,用于标记两个动画是否都已完成。在AnimateRectange方法中,我在调用Thread.Sleep的while循环中检查了它们。这个循环从未完成,所以它肯定在同一个线程上

因此,我创建了一个队列来按顺序处理动画:

    private void ProcessAnimationQueue()
    {
        var items = this.m_animationQueue.GetEnumerator();
        while (items.MoveNext())
        {
            while (this.m_isXanimationInProgress || this.m_isYanimationInProgress)
            {
                System.Threading.Thread.Sleep(100);
            }

            var item = items.Current;
            Dispatcher.BeginInvoke(() => this.AnimateRectangle(item.Rect.Rect, item.X, item.Y));                
        }
    }

然后我调用我的初始例程,该例程对动画进行排队,并在新线程上调用此方法。我看到了相同的结果。

据我所知,Silverlight中的所有动画都是在UI线程上进行的。我猜无论如何只有UI线程在调用这个动画函数,所以我不确定使用锁定是否会有帮助。你真的想阻止整个线程还是仅仅阻止另一个动画开始

我建议采取类似的措施:

private bool isAnimating = false;

public void AnimateRectangle(Rectangle rect, double newX, double newY)
{
    if (isAnimating)
        return;

    // rest of animation code

    sb.Completed += (sender, e) =>
    {
        isAnimating = false;
    };

    isAnimating = true;
    sb.Begin();
}

只需跟踪您当前是否正在使用标志设置动画,如果需要,请提前返回。如果您不想丢失潜在的动画,您的另一个选择是保留某种类型的动画队列,您可以在每个动画完成后检查/启动该队列。

据我所知,Silverlight中的所有动画都发生在UI线程上。我猜无论如何只有UI线程在调用这个动画函数,所以我不确定使用锁定是否会有帮助。你真的想阻止整个线程还是仅仅阻止另一个动画开始

我建议采取类似的措施:

private bool isAnimating = false;

public void AnimateRectangle(Rectangle rect, double newX, double newY)
{
    if (isAnimating)
        return;

    // rest of animation code

    sb.Completed += (sender, e) =>
    {
        isAnimating = false;
    };

    isAnimating = true;
    sb.Begin();
}

只需跟踪您当前是否正在使用标志设置动画,如果需要,请提前返回。如果你不想丢失潜在的动画,你的另一个选择是为动画保留某种队列,你可以在每个动画完成后检查/启动这些队列。

这个问题让我非常感兴趣。事实上,我将在我的下一篇博文中包含它

简单地说,为了确保我们讨论的是同一件事,基本上你不想阻止对AnimateRectangle的调用,你只想对调用进行排队,以便在任何未完成的调用完成其动画后,执行该排队调用。如果前一个通话尚未开始,您可能需要将多个通话排队

因此,我们需要两件事:-

将本质上是异步操作的一种方法。sb.Begin to Completed事件视为顺序操作,一个操作仅在前一个操作完成时开始。 当一个或多个操作尚未完成时,将附加操作排队的一种方法。 异步操作服务

项目1在Silverlight中以无数种不同的方式出现,这是因为有很多东西是异步的。我通过一个简单的异步运行程序解决了这个问题。将AsyncOperationService代码添加到项目中

异步操作队列

它的项目2真的引起了我的兴趣。这里的变化是,当一组现有操作正在进行时,需要添加另一组操作。对于一般情况下的解决方案,我们需要一种线程安全的方法来包含另一个操作

以下是AsyncOperationQueue的基本内容:-

我们需要保存AsyncOperationQueue的一个实例:-

最后,我们需要重新创建AnimateRectangle,将操作排入队列:-

 public void AnimateRectangle(Rectangle rect, double newX, double newY)  
 {
     myAnimationQueue.Enqueue(GetAnimateRectangleOp(rect, newX, newY)
 }

这个问题真的引起了我极大的兴趣。事实上,我要把它包括在我的n中 下一篇博文

简单地说,为了确保我们讨论的是同一件事,基本上你不想阻止对AnimateRectangle的调用,你只想对调用进行排队,以便在任何未完成的调用完成其动画后,执行该排队调用。如果前一个通话尚未开始,您可能需要将多个通话排队

因此,我们需要两件事:-

将本质上是异步操作的一种方法。sb.Begin to Completed事件视为顺序操作,一个操作仅在前一个操作完成时开始。 当一个或多个操作尚未完成时,将附加操作排队的一种方法。 异步操作服务

项目1在Silverlight中以无数种不同的方式出现,这是因为有很多东西是异步的。我通过一个简单的异步运行程序解决了这个问题。将AsyncOperationService代码添加到项目中

异步操作队列

它的项目2真的引起了我的兴趣。这里的变化是,当一组现有操作正在进行时,需要添加另一组操作。对于一般情况下的解决方案,我们需要一种线程安全的方法来包含另一个操作

以下是AsyncOperationQueue的基本内容:-

我们需要保存AsyncOperationQueue的一个实例:-

最后,我们需要重新创建AnimateRectangle,将操作排入队列:-

 public void AnimateRectangle(Rectangle rect, double newX, double newY)  
 {
     myAnimationQueue.Enqueue(GetAnimateRectangleOp(rect, newX, newY)
 }
 public void AnimateRectangle(Rectangle rect, double newX, double newY)  
 {
     myAnimationQueue.Enqueue(GetAnimateRectangleOp(rect, newX, newY)
 }