我正在异步/并发处理代理服务器列表,测试每个代理服务器的有效性。这些代理服务器显示在一个自定义用户控件中,该控件继承自DataGridView,并在其构造函数中将DoubleBuffered属性设置为true。此外,该DGV不受数据限制,而是使用虚拟模式和CellValueNeeded。
在我测试显示代理(ProxyTester.Start())有效性的方法中,我可以使用SemaphoreSlim对象控制并发度。当使用像10这样的小值初始化该信号量时,我可以滚动DGV并查看正在更新的数据并且它是平滑的。如果我将并发度增加到更大的数字(如100),这会增加吞吐量(耶!),我的DGV在滚动期间开始滞后。
除了我已经完成的将DoubleBuffered设置为True并使用虚拟模式之外,如何在滚动/处理列表的过程中减少延迟,同时还具有高度的并发性?
public partial class ExtendedDataGridView : DataGridView
{
public ExtendedDataGridView()
{
//InitializeComponent();
DoubleBuffered = true;
}
}
public partial class DataGridViewForm : Form
{
private List<Proxy> proxies = new List<Proxy>();
public DataGridViewForm()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10000; i++)
{
proxies.Add(new Proxy("127.0.0." + RandomUtility.GetRandomInt(1, 5), 8888));
}
extendedDataGridView1.RowCount = proxies.Count;
}
private void button2_Click(object sender, EventArgs e)
{
var judges = new List<ProxyJudge>();
judges.Add(new ProxyJudge("http://azenv.net"));
Task.Run(async () => { await ProxyTester.Start(proxies, judges, maxConcurrency: 1); });
}
private void extendedDataGridView1_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
if (proxies.Count > 0)
{
var proxy = proxies[e.RowIndex];
switch (e.ColumnIndex)
{
case 0:
e.Value = proxy.IP;
break;
case 1:
e.Value = proxy.IsValid;
break;
default:
break;
}
}
}
}
public class ProxyTester
{
public async static 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>();
}
//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();
var count = 0;
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())));
semaphore.Release();
Interlocked.Increment(ref count);
Console.WriteLine(count);
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);
}
}