C# 在任务中动态暂停线程

C# 在任务中动态暂停线程,c#,task-parallel-library,C#,Task Parallel Library,我的任务是添加一个滑块控件,其值是加速或减慢生成值的任务中的内容的输出。我目前有一个任务,它运行一个无限while循环,并将属性设置回ui。此外,我还在线程睡眠中硬编码一个值,以控制输出的速度。我想知道我是否可以在Threas.Sleep中创建该变量,而不是硬编码,并且能够在任务进行时更改该变量。我有一个想法,当滑块改变时,我可以取消任务,并使用滑块控件的值重新启动它。任务代码如下所示: Task.Factory.StartNew(() => {

我的任务是添加一个滑块控件,其值是加速或减慢生成值的任务中的内容的输出。我目前有一个任务,它运行一个无限while循环,并将属性设置回ui。此外,我还在线程睡眠中硬编码一个值,以控制输出的速度。我想知道我是否可以在Threas.Sleep中创建该变量,而不是硬编码,并且能够在任务进行时更改该变量。我有一个想法,当滑块改变时,我可以取消任务,并使用滑块控件的值重新启动它。任务代码如下所示:

Task.Factory.StartNew(() => 
            {
                while (true)
                {
                    App.Current.Dispatcher.Invoke(new Action(() => 
                    {
                        //Some number generated and set to a property
                    }));
                    Thread.Sleep(200);
                }
            });

问题是我是否可以将Thread.Sleep(200)更改为Thread.Sleep(SpeedVariable),可以在其他地方设置它只需使用一个全局变量,并将其标记即可

通常,您必须在这样的共享变量周围放置互斥体,但在大多数情况下。如果您希望绝对安全,或者如果您计划移植到其他操作系统,或者如果您想使用
long
而不是
int
,那么您应该将读取和更新包含在一个块中,或者使用,如下所示:

Interlocked.Exchange(ref WaitInterval, mySlider.Value);

只需使用一个全局变量,并标记它

通常,您必须在这样的共享变量周围放置互斥体,但在大多数情况下。如果您希望绝对安全,或者如果您计划移植到其他操作系统,或者如果您想使用
long
而不是
int
,那么您应该将读取和更新包含在一个块中,或者使用,如下所示:

Interlocked.Exchange(ref WaitInterval, mySlider.Value);

John Wu的解决方案会起作用,但理想情况下,您不应该使用
Thread.Sleep
,尤其是在任务中,因为它们并不总是保证在单独的线程上运行

相反,您可以使用TPL的内置延迟功能:

static public volatile int WaitInterval = 200;
public void StartLoop()
{
    Task.Factory.StartNew(async () =>
    {
        while (true)
        {
            App.Current.Dispatcher.Invoke(new Action(() =>
            {
                    //Some number generated and set to a property
                }));
            await Task.Delay(WaitInterval);
        }
    });
}

protected mySlider_ValueChanged(object sender, SliderEventArgs e)
{
    WaitInterval = mySlider.Value;
}
正如斯维克所指出的那样。在任务中使用异步/等待时,请使用:

“Task.Run”而不是“Task.Factory.StartNew”


但是,如果确实遇到“
任务”
,则可以在任务实例上使用“.Unwrap()”方法

John Wu的解决方案会起作用,但理想情况下,您不应该使用
Thread.Sleep
,尤其是在任务中,因为它们并不总是保证在单独的线程上运行

相反,您可以使用TPL的内置延迟功能:

static public volatile int WaitInterval = 200;
public void StartLoop()
{
    Task.Factory.StartNew(async () =>
    {
        while (true)
        {
            App.Current.Dispatcher.Invoke(new Action(() =>
            {
                    //Some number generated and set to a property
                }));
            await Task.Delay(WaitInterval);
        }
    });
}

protected mySlider_ValueChanged(object sender, SliderEventArgs e)
{
    WaitInterval = mySlider.Value;
}
正如斯维克所指出的那样。在任务中使用异步/等待时,请使用:

“Task.Run”而不是“Task.Factory.StartNew”


但是,如果确实遇到“
任务”
,则可以在任务实例上使用“.Unwrap()”方法

您最好避免使用任务,并使用Microsoft的反应式框架(WPF位使用NuGet“System.Reactive.Windows.Threading”,标准位使用“System.Reactive”)

这使这种事情的生活变得容易多了。首先定义
static public volatile int SpeedVariable=200然后尝试以下操作:

IDisposable subscription =
    Observable
        .Generate(0, x => true, x => x + 1, x => x,
            x => TimeSpan.FromMilliseconds(SpeedVariable))
        .ObserveOnDispatcher()
        .Subscribe(x =>
        {
            //Some number generated and set to a property
        });
通过调用
subscription.Dispose(),您可以随时停止可观察对象

您甚至应该能够使用
.Subscribe
方法中的值
x
来计算滑块的值。在该代码中,该值以
0
开始,并为产生的每个值增加
1


以下是可以运行以查看其工作情况的代码版本:

void Main()
{
    IDisposable subscription =
        Observable
            .Generate(0, x => true, x => x + 1, x => x,
                x => TimeSpan.FromMilliseconds(SpeedVariable))
            .ObserveOn(Scheduler.Default)
            .Subscribe(x => Console.WriteLine(x));

    Thread.Sleep(1000);
    SpeedVariable = 1000;
    Thread.Sleep(5000);
    SpeedVariable = 20;
    Thread.Sleep(500);
    subscription.Dispose();
}

static public volatile int SpeedVariable = 200;
此外,如果您希望避免使用
静态公共volatile
变量,那么这也可以:

var speed = new ReplaySubject<int>(1);

IDisposable subscription =
    Observable
        .Generate(0, x => true, x => x + 1, x => x,
            x => TimeSpan.FromMilliseconds(speed.MostRecent(200).First()))
        .ObserveOn(Scheduler.Default)
        .Subscribe(x => Console.WriteLine(x));

Thread.Sleep(1000);
speed.OnNext(1000);
Thread.Sleep(5000);
speed.OnNext(20);
Thread.Sleep(500);
subscription.Dispose();
var-speed=新的ReplaySubject(1);
IDisposable订阅=
可观察
.Generate(0,x=>true,x=>x+1,x=>x,
x=>TimeSpan.From毫秒(speed.MostRecent(200.First()))
.ObserveOn(Scheduler.Default)
.Subscribe(x=>Console.WriteLine(x));
睡眠(1000);
速度.OnNext(1000);
睡眠(5000);
速度.OnNext(20);
睡眠(500);
subscription.Dispose();

现有代码的一个可能的复杂性,到目前为止的其他答案,以及我上面的答案,是您可以暂停线程太长时间,并且无法让线程在不等待前一次暂停的情况下重新启动。Rx提供了一种解决此问题的简单方法。请尝试以下代码:

var speed = new Subject<int>();

IDisposable subscription =
    speed
        .Select(s => Observable.Interval(TimeSpan.FromMilliseconds(s)))
        .Switch()
        .Select((x, n) => n)
        .ObserveOn(Scheduler.Default)
        .Subscribe(x => Console.WriteLine(x));

speed.OnNext(200);
Thread.Sleep(1000);
speed.OnNext(1000000); // wait 16.666 minutes
Thread.Sleep(5000);
speed.OnNext(20); // stop previous wait
Thread.Sleep(500);
subscription.Dispose();
var speed=新主题();
IDisposable订阅=
速度
.Select(s=>Observable.Interval(TimeSpan.Fromms(s)))
.Switch()
.选择((x,n)=>n)
.ObserveOn(Scheduler.Default)
.Subscribe(x=>Console.WriteLine(x));
速度.OnNext(200);
睡眠(1000);
速度.OnNext(1000000);//等16.666分钟
睡眠(5000);
速度.OnNext(20);//停止之前的等待
睡眠(500);
subscription.Dispose();

您最好避免使用任务,并使用Microsoft的反应式框架(WPF位使用NuGet“System.Reactive.Windows.Threading”,标准位使用“System.Reactive”)

这使这种事情的生活变得容易多了。首先定义
static public volatile int SpeedVariable=200然后尝试以下操作:

IDisposable subscription =
    Observable
        .Generate(0, x => true, x => x + 1, x => x,
            x => TimeSpan.FromMilliseconds(SpeedVariable))
        .ObserveOnDispatcher()
        .Subscribe(x =>
        {
            //Some number generated and set to a property
        });
通过调用
subscription.Dispose(),您可以随时停止可观察对象

您甚至应该能够使用
.Subscribe
方法中的值
x
来计算滑块的值。在该代码中,该值以
0
开始,并为产生的每个值增加
1


以下是可以运行以查看其工作情况的代码版本:

void Main()
{
    IDisposable subscription =
        Observable
            .Generate(0, x => true, x => x + 1, x => x,
                x => TimeSpan.FromMilliseconds(SpeedVariable))
            .ObserveOn(Scheduler.Default)
            .Subscribe(x => Console.WriteLine(x));

    Thread.Sleep(1000);
    SpeedVariable = 1000;
    Thread.Sleep(5000);
    SpeedVariable = 20;
    Thread.Sleep(500);
    subscription.Dispose();
}

static public volatile int SpeedVariable = 200;
此外,如果您希望避免使用
静态公共volatile
变量,那么这也可以:

var speed = new ReplaySubject<int>(1);

IDisposable subscription =
    Observable
        .Generate(0, x => true, x => x + 1, x => x,
            x => TimeSpan.FromMilliseconds(speed.MostRecent(200).First()))
        .ObserveOn(Scheduler.Default)
        .Subscribe(x => Console.WriteLine(x));

Thread.Sleep(1000);
speed.OnNext(1000);
Thread.Sleep(5000);
speed.OnNext(20);
Thread.Sleep(500);
subscription.Dispose();
var-speed=新的ReplaySubject(1);
IDisposable订阅=
可观察
.Generate(0,x=>true,x=>x+1,x=>x,
x=>TimeSpan.From毫秒(speed.MostRecent(200.First()))
.ObserveOn(Scheduler.Default)
.Subscribe(x=>Console.WriteLine(x));
睡眠(1000);
速度.OnNext(1000);
睡眠(5000);
速度.OnNext(20);
睡眠(500);
subscription.Dispose();

现有代码的一个可能的复杂性是另一个答案