在 Parallel.ForEach 循环中,我想增加一个 var 但 Interlock.Increment 似乎不起作用
Within Parallel.ForEach loop, I want to increment a var but Interlock.Increment doesn't seem to work
我有一个需要很长时间的进程,所以我想将它分解为多个线程。我的线程方法适用于 Parallel.ForEach
,只是我想通知用户到目前为止我们已经处理了多少可变数量的项目。
这是我正在做的示例。
namespace TestingThreading
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
private int _idCounter;
public int IdCounter
{
get { return _idCounter; }
set
{
if (value != _idCounter)
{
_idCounter = value;
OnPropertyChanged("IdCounter");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string name)
{
var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null);
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public MainWindow()
{
InitializeComponent();
Counter.SetBinding(ContentProperty, new Binding("IdCounter"));
DataContext = this;
IdCounter = 0;
}
//random wait as a stand in for a variable length task
private void GetWait()
{
Random random = new Random();
int w = random.Next(3, 15);
System.Threading.Thread.Sleep(100 * w);
}
private async void CreateClientsButton_Click(object sender, RoutedEventArgs e)
{
//setup my a list of strings to iterate through
List<String> DeviceList = new List<string>();
for (int i = 0; i < 150; i++)
{
string ThisClient = "Fox" + i;
DeviceList.Add(ThisClient);
}
var myTask = Task.Run(() =>
{
Parallel.ForEach(DeviceList, new ParallelOptions { MaxDegreeOfParallelism = 15 }, device =>
{
GetWait();
IdCounter++;
// both below give compiler errors
//System.Threading.Interlocked.Add(ref IdCounter, 1);
//var c = Interlocked.Increment(ref DeviceCounter);
});
});
await myTask;
}
}
}
绑定到 UI 有效,但我很确定我实际上并没有像我应该的那样多次递增变量。例如,这将 运行 150 次迭代,但经过多次尝试后,我从未见过我的计数器结束高于 146,这让我相信当两个线程尝试同时更新变量时存在竞争条件.
这不是世界末日,但我很想这样做 'the right way',我的研究将我引向 Interlock.Increment
或 Interlock.Add
,但是当我尝试用其中任何一个增加我的变量,我得到这个错误:
A property or indexer may not be passed as an out or ref parameter
我强烈建议使用 IProgress<T>
来更新 UI。使用 "stateful" 进度(即 "increment")而不是 "stateless" 进度(即 "Item 13 has completed.")是不常见的,但它是可行的。
请注意 Progress<T>
负责与 UI 线程同步,因此它为您解决了竞争条件。
var progress = new Progress<int>(_ => IdCounter++) as IProgress<int>;
var myTask = Task.Run(() =>
{
Parallel.ForEach(DeviceList, new ParallelOptions { MaxDegreeOfParallelism = 15 }, device =>
{
GetWait();
progress.Report(0);
});
});
我有一个需要很长时间的进程,所以我想将它分解为多个线程。我的线程方法适用于 Parallel.ForEach
,只是我想通知用户到目前为止我们已经处理了多少可变数量的项目。
这是我正在做的示例。
namespace TestingThreading
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
private int _idCounter;
public int IdCounter
{
get { return _idCounter; }
set
{
if (value != _idCounter)
{
_idCounter = value;
OnPropertyChanged("IdCounter");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string name)
{
var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null);
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public MainWindow()
{
InitializeComponent();
Counter.SetBinding(ContentProperty, new Binding("IdCounter"));
DataContext = this;
IdCounter = 0;
}
//random wait as a stand in for a variable length task
private void GetWait()
{
Random random = new Random();
int w = random.Next(3, 15);
System.Threading.Thread.Sleep(100 * w);
}
private async void CreateClientsButton_Click(object sender, RoutedEventArgs e)
{
//setup my a list of strings to iterate through
List<String> DeviceList = new List<string>();
for (int i = 0; i < 150; i++)
{
string ThisClient = "Fox" + i;
DeviceList.Add(ThisClient);
}
var myTask = Task.Run(() =>
{
Parallel.ForEach(DeviceList, new ParallelOptions { MaxDegreeOfParallelism = 15 }, device =>
{
GetWait();
IdCounter++;
// both below give compiler errors
//System.Threading.Interlocked.Add(ref IdCounter, 1);
//var c = Interlocked.Increment(ref DeviceCounter);
});
});
await myTask;
}
}
}
绑定到 UI 有效,但我很确定我实际上并没有像我应该的那样多次递增变量。例如,这将 运行 150 次迭代,但经过多次尝试后,我从未见过我的计数器结束高于 146,这让我相信当两个线程尝试同时更新变量时存在竞争条件.
这不是世界末日,但我很想这样做 'the right way',我的研究将我引向 Interlock.Increment
或 Interlock.Add
,但是当我尝试用其中任何一个增加我的变量,我得到这个错误:
A property or indexer may not be passed as an out or ref parameter
我强烈建议使用 IProgress<T>
来更新 UI。使用 "stateful" 进度(即 "increment")而不是 "stateless" 进度(即 "Item 13 has completed.")是不常见的,但它是可行的。
请注意 Progress<T>
负责与 UI 线程同步,因此它为您解决了竞争条件。
var progress = new Progress<int>(_ => IdCounter++) as IProgress<int>;
var myTask = Task.Run(() =>
{
Parallel.ForEach(DeviceList, new ParallelOptions { MaxDegreeOfParallelism = 15 }, device =>
{
GetWait();
progress.Report(0);
});
});