C# 进度计数器有时不会递增到预期值?
C# Progress Counter sometimes not incrementing to expected value?
我正在测试列表代理的有效性。每次测试代理时,我都会递增一个名为 Progress 的 int 类型的 属性。如果我测试一个包含 100 个代理的列表,当所有任务都完成时,有时 Progress 会等于 100,但有时它不会是预期值 100,而是 97 或 98(它会变化)。
public class ProxyTester
{
public int Progress { get; set; }
public async Task Start(List<Proxy> proxies, List<ProxyJudge> judges, List<ProxyTest> tests = null, PauseOrCancelToken pct = null, int maxConcurrency = 100)
{
if (tests == null)
{
tests = new List<ProxyTest>();
}
this.Progress = 0;
//Get external IP to check if proxy is anonymous.
var publicIp = await WebUtility.GetPublicIP();
//Validate proxy judges.
var tasks = new List<Task>();
var semaphore = new SemaphoreSlim(maxConcurrency);
foreach (var judge in judges)
{
tasks.Add(Task.Run(async () => {
await semaphore.WaitAsync();
judge.IsValid = await judge.TestValidityAsync();
if (pct != null) { await pct.PauseOrCancelIfRequested(); }
semaphore.Release();
}));
}
await Task.WhenAll(tasks);
var validJudges = from judge in judges
where judge.IsValid
select judge;
if (validJudges.Count() == 0)
{
throw new Exception("No valid judges loaded.");
}
//Validate proxy tests.
tasks.Clear();
foreach (var test in tests)
{
tasks.Add(Task.Run(async () => {
await semaphore.WaitAsync();
test.IsValid = await test.TestValidityAsync();
if (pct != null) { await pct.PauseOrCancelIfRequested(); }
semaphore.Release();
}));
}
await Task.WhenAll(tasks);
var validTests = from test in tests
where test.IsValid
select test;
//Test proxies with a random, valid proxy judge. If valid, test with all valid proxy tests.
tasks.Clear();
foreach (var proxy in proxies)
{
tasks.Add(Task.Run(async () =>
{
await semaphore.WaitAsync();
proxy.IsValid = await proxy.TestValidityAsync(validJudges.ElementAt(RandomUtility.GetRandomInt(0, validJudges.Count())));
this.Progress++;
Console.WriteLine(this.Progress + " / " + proxies.Count);
if (pct != null) { await pct.PauseOrCancelIfRequested(); }
semaphore.Release();
if (proxy.IsValid)
{
proxy.TestedSites.AddRange(validTests);
var childTasks = new List<Task>();
foreach (var test in validTests)
{
childTasks.Add(Task.Run(async () =>
{
await semaphore.WaitAsync();
proxy.TestedSites.ElementAt(proxy.TestedSites.IndexOf(test)).IsValid = await proxy.TestValidityAsync(test);
if (pct != null) { await pct.PauseOrCancelIfRequested(); }
semaphore.Release();
}));
}
await Task.WhenAll(childTasks);
}
}));
}
await Task.WhenAll(tasks);
}
}
问题是来自多个线程的变量更新不是原子的。为了让多个线程更新该变量,您需要将其标记为 volatile
并使用类似 Interlocked.Increment
的同步方法。
由于必须通过ref
传递变量,因此需要创建一个支持字段:
private volatile int _progress = 0;
public int Progress => _progress;
然后,而不是
this.Progress++;
您可以将其更改为联锁调用:
Interlocked.Increment(ref _progress);
现在多个线程可以更新它而不会有冲突的风险。您 运行 遇到的问题是一个线程读取该值,两个线程更新它,覆盖一些更新。这就是为什么它看起来 "random",这取决于覆盖哪些和多少的时间。
我正在测试列表代理的有效性。每次测试代理时,我都会递增一个名为 Progress 的 int 类型的 属性。如果我测试一个包含 100 个代理的列表,当所有任务都完成时,有时 Progress 会等于 100,但有时它不会是预期值 100,而是 97 或 98(它会变化)。
public class ProxyTester
{
public int Progress { get; set; }
public async Task Start(List<Proxy> proxies, List<ProxyJudge> judges, List<ProxyTest> tests = null, PauseOrCancelToken pct = null, int maxConcurrency = 100)
{
if (tests == null)
{
tests = new List<ProxyTest>();
}
this.Progress = 0;
//Get external IP to check if proxy is anonymous.
var publicIp = await WebUtility.GetPublicIP();
//Validate proxy judges.
var tasks = new List<Task>();
var semaphore = new SemaphoreSlim(maxConcurrency);
foreach (var judge in judges)
{
tasks.Add(Task.Run(async () => {
await semaphore.WaitAsync();
judge.IsValid = await judge.TestValidityAsync();
if (pct != null) { await pct.PauseOrCancelIfRequested(); }
semaphore.Release();
}));
}
await Task.WhenAll(tasks);
var validJudges = from judge in judges
where judge.IsValid
select judge;
if (validJudges.Count() == 0)
{
throw new Exception("No valid judges loaded.");
}
//Validate proxy tests.
tasks.Clear();
foreach (var test in tests)
{
tasks.Add(Task.Run(async () => {
await semaphore.WaitAsync();
test.IsValid = await test.TestValidityAsync();
if (pct != null) { await pct.PauseOrCancelIfRequested(); }
semaphore.Release();
}));
}
await Task.WhenAll(tasks);
var validTests = from test in tests
where test.IsValid
select test;
//Test proxies with a random, valid proxy judge. If valid, test with all valid proxy tests.
tasks.Clear();
foreach (var proxy in proxies)
{
tasks.Add(Task.Run(async () =>
{
await semaphore.WaitAsync();
proxy.IsValid = await proxy.TestValidityAsync(validJudges.ElementAt(RandomUtility.GetRandomInt(0, validJudges.Count())));
this.Progress++;
Console.WriteLine(this.Progress + " / " + proxies.Count);
if (pct != null) { await pct.PauseOrCancelIfRequested(); }
semaphore.Release();
if (proxy.IsValid)
{
proxy.TestedSites.AddRange(validTests);
var childTasks = new List<Task>();
foreach (var test in validTests)
{
childTasks.Add(Task.Run(async () =>
{
await semaphore.WaitAsync();
proxy.TestedSites.ElementAt(proxy.TestedSites.IndexOf(test)).IsValid = await proxy.TestValidityAsync(test);
if (pct != null) { await pct.PauseOrCancelIfRequested(); }
semaphore.Release();
}));
}
await Task.WhenAll(childTasks);
}
}));
}
await Task.WhenAll(tasks);
}
}
问题是来自多个线程的变量更新不是原子的。为了让多个线程更新该变量,您需要将其标记为 volatile
并使用类似 Interlocked.Increment
的同步方法。
由于必须通过ref
传递变量,因此需要创建一个支持字段:
private volatile int _progress = 0;
public int Progress => _progress;
然后,而不是
this.Progress++;
您可以将其更改为联锁调用:
Interlocked.Increment(ref _progress);
现在多个线程可以更新它而不会有冲突的风险。您 运行 遇到的问题是一个线程读取该值,两个线程更新它,覆盖一些更新。这就是为什么它看起来 "random",这取决于覆盖哪些和多少的时间。