我的任务是添加一个slidercontrol,其值可以加快或减慢生成值的任务中的内容的输出。我目前有一个正在运行无限循环并将属性设置回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)
答案 0 :(得分:2)
您最好避免使用任务并使用Microsoft的Reactive Framework(NuGet" System.Reactive.Windows.Threading"用于WPF位,或者" 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();
停止观察。
您甚至可以使用x
方法中的值.Subscribe
来计算滑块的值。在此代码中,值以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;
此外,如果您想避免使用static public 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();
现有代码的一个可能的复杂性,到目前为止的其他答案,以及我上面的答案,是你可以暂停线程太长时间,没有办法让线程重新开始而不等待上次停顿。 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();
答案 1 :(得分:1)
只需使用全局变量,并将其标记为volatile。
static public volatile int WaitInterval = 200;
public void StartLoop()
{
Task.Factory.StartNew(() =>
{
while (true)
{
App.Current.Dispatcher.Invoke(new Action(() =>
{
//Some number generated and set to a property
}));
Thread.Sleep(WaitInterval);
}
});
}
protected mySlider_ValueChanged(object sender, SliderEventArgs e)
{
WaitInterval = mySlider.Value;
}
通常,你必须在这样的共享变量周围加一个互斥锁,但在大多数情况下都是integer updates are automatically atomic。如果您想要绝对安全,或者您打算移植到其他操作系统,或者如果您想使用long
而不是int
,那么您可以附上阅读和在lock块内更新,或使用Interlocked.Exchange,如下所示:
Interlocked.Exchange(ref WaitInterval, mySlider.Value);
答案 2 :(得分:-1)
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;
}
正如斯维克指出的那样。在任务中使用async / await时,请使用:
&#39; Task.Run&#39;而不是&#39; Task.Factory.StartNew&#39;。
如果您遇到&#39; Task<Task<T>>
&#39;但是,您可以使用&#39; .Unwrap()&#39;任务实例上的方法。