- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我最近开始学习 C#,但在更新 UI 时遇到了运行多线程的问题。根据我目前所学,SemaphoreSlim
似乎是运行多线程同时仍控制最大并发线程数的正确方法。
场景:我想向网站(例如http://www.somesite.com/keyword
)发送GET请求,并使用返回的字符串来获取大量的关键字。在运行所有线程时,在每个线程之后我想更新 UI,比如计算好结果和坏结果。到目前为止,这是我的代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
static SemaphoreSlim _pool;
static readonly int maxThreads = 4;// able to go up to 100-200
static List<string> keysList = new List<string>();
static List<string> liveKeys = new List<string>();
static List<string> deadKeys = new List<string>();
static Queue<string> keysQueue = new Queue<string>();
static int liveCount = 0;
static int deadCount = 0;
public Form1()
{
InitializeComponent();
getAllKeyWordsToList();// get keywords from file
}
private void startButton_Click(object sender, EventArgs e)
{
_pool = new SemaphoreSlim(maxThreads);
foreach (string keyWord in keysList)
{
keysQueue.Enqueue(keyWord);
}
foreach (string key in keysList)
{
new Thread(Worker).Start(key);
keysQueue.Dequeue();
}
// this part is skipped when startButton is pressed
// tried with looks but I'm too stupid or too new with C#
while(keysQueue.Count() > 0)
{
// update UI
this.statusLabel.Text = "Only " + keysQueue.Count().ToString() + " keywords left";
this.liveLabel.Text = liveLabel.ToString();
this.deadLabel.Text = deadLabel.ToString();
}
// this get's updated; only this...
// while threads are still running
this.statusLabel.Text = "Finished!";
}
private void Worker(object obj)
{
var uri = "http://www.somesite.com/";
var key = obj.ToString();
using(var wc = new WebClient())
{
string result = wc.DownloadString(uri + key);
_pool.Wait();
if (result.Contains("live"))
{
// do some more work with the result
liveCount++;
}
else
{
// do some work with the result
deadCount++;
}
_pool.Release();
}
}
}
}
最佳答案
你不需要,也不应该在这里使用 SemaphoreSlim
。
您的代码跳过 while
循环的原因是您在到达那里时已经清空了队列。这个循环:
foreach (string key in keysList)
{
new Thread(Worker).Start(key);
keysQueue.Dequeue();
}
…为 keysList
集合中的每个元素启动一个新线程,并从队列中删除一个项目(甚至不使用该值!)。这些都不会以任何方式阻止;线程独立于循环启动,并且不影响循环的进度。所以循环完成,很可能甚至在单个线程开始运行之前,但在任何情况下几乎可以肯定在任何线程完成之前,代码继续执行 while
循环,其中 keysQueue
集合已被上面的前一个 foreach
循环清空。
由于此时队列的Count
已经为0,因此永远不会进入while
循环。条件在第一次尝试执行时已经是 false
。
除此之外,您的代码还有许多其他方面低于标准。最大的问题是,除了您尝试在不需要信号量的地方使用信号量之外,代码为每个事件操作分配了一个完整的线程,并且它阻塞了 UI(或者,至少,会 如果代码完成了您想要的操作,则在整个处理过程继续进行时阻止 UI。
WebClient
类有一个DownloadStringAsync()
方法,可用于以“可等待”的方式异步执行每个下载操作。 “等待”意味着该方法可以在操作完成之前返回,从而允许当前线程在操作进行时继续工作(即在这种情况下,它可以处理 UI 更新,甚至可以处理用户输入(如果需要)。
通过使用此异步版本的 DownloadString()
方法,并在任务开始时跟踪任务,可以轻松编写一个直接循环来完成您想要的所有处理,将操作限制为您想要的任何最大并发操作,所有这些都不会阻塞 UI 线程。
这是我的意思的一个例子:
public partial class Form1 : Form
{
private const int _kmaxTasks = 4;
private readonly List<string> _keysList =
new List<string> { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" };
private readonly List<string> _liveKeys = new List<string>();
private readonly List<string> _deadKeys = new List<string>();
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
// Initialize a new queue, copying all elements from _keysList to the queue
Queue<string> keysQueue = new Queue<string>(_keysList);
// List of tasks, to keep track of active tasks
//
// NOTE: value tuple syntax requires that you use the NuGet Package Manager
// to get Microsoft's "ValueTuple" package installed for your project.
List<Task<(bool, string)>> tasks = new List<Task<(bool, string)>>();
button1.Enabled = false;
_liveKeys.Clear();
_deadKeys.Clear();
_UpdateStatus(keysQueue.Count, _liveKeys.Count, _deadKeys.Count);
// Keep working until we're out of keys *and* out of tasks
while (keysQueue.Count > 0 || tasks.Count > 0)
{
// If we've got the max number of tasks running already, wait for one to
// complete. Even if we don't have the max number of tasks running, if we're
// out of keys also wait for one to complete.
if (tasks.Count >= _kmaxTasks || keysQueue.Count == 0)
{
Task<(bool Live, string Key)> completedTask = await Task.WhenAny(tasks);
tasks.Remove(completedTask);
if (completedTask.Result.Live)
{
_liveKeys.Add(completedTask.Result.Key);
}
else
{
_deadKeys.Add(completedTask.Result.Key);
}
_UpdateStatus(
keysQueue.Count + tasks.Count, _liveKeys.Count, _deadKeys.Count);
}
if (keysQueue.Count > 0)
{
tasks.Add(Worker(keysQueue.Dequeue()));
}
}
statusLabel.Text = "Finished!";
button1.Enabled = true;
}
private void _UpdateStatus(int count, int liveCount, int deadCount)
{
statusLabel.Text = $"Only {count} keywords left";
liveLabel.Text = liveCount.ToString();
deadLabel.Text = deadCount.ToString();
}
private async Task<(bool, string)> Worker(string key)
{
string uri = "http://www.somesite.com/";
using (MockWebClient wc = new MockWebClient())
{
string result = await wc.DownloadStringAsync(uri + key);
return (result.Contains("live"), key);
}
}
}
请注意,除了将代码转换为使用异步操作,当然还有正确工作之外,所有这些本身都大大简化了代码,我还删除了不必要的变量并将队列对象移动到Click
事件处理程序,仅由局部变量引用。
我还稍微重构了 Worker
,恕我直言,Worker()
方法只进行下载和检查 更有意义>"live"
,然后让调用者根据结果进行记账。为此,我还使用了新的 C# 值元组功能,它允许我使用简化的内置语法从方法中返回一对值来表示它们。
最后,由于您的问题不包括任何特定的网络服务器或实际可用于测试代码的关键值(为了将来引用,请注意需要提供一个好的Minimal, Complete, and Verifiable code example 正确说明了您的问题),我使用了一些虚拟测试数据,并编写了一个简单的 MockWebClient
,它具有相同的 DownloadStringAsync()
方法,但实现只是假装做一些工作并返回一个结果,使用随机数生成器来确定操作的持续时间和结果本身:
class MockWebClient : IDisposable
{
private static readonly TimeSpan _kminDelay = TimeSpan.FromSeconds(1);
private static readonly TimeSpan _kmaxDelay = TimeSpan.FromSeconds(5);
private static readonly Random _random = new Random();
private static readonly object _lock = new object();
private static TimeSpan _NextRandomDelay(TimeSpan min, TimeSpan max)
{
lock (_lock)
{
return TimeSpan.FromSeconds(
(max.TotalSeconds - min.TotalSeconds) * _random.NextDouble());
}
}
private static bool _NextRandomBool()
{
lock (_lock)
{
return _random.Next(2) == 1;
}
}
public async Task<string> DownloadStringAsync(string uri)
{
await Task.Delay(_NextRandomDelay(_kminDelay, _kmaxDelay));
return _NextRandomBool() ? "live" : "dead";
}
public void Dispose()
{
// do nothing...it's a mock!
}
}
显然,您的实际程序不需要该类。它的存在只是为了让您(和我)可以运行代码并查看它的工作情况,而无需处理真正的 Web 服务器。
最后,为了完整起见,这里是*.Designer.cs
代码:
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.liveLabel = new System.Windows.Forms.Label();
this.deadLabel = new System.Windows.Forms.Label();
this.statusLabel = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(13, 13);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(182, 60);
this.button1.TabIndex = 0;
this.button1.Text = "Start";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(40, 110);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(83, 32);
this.label1.TabIndex = 1;
this.label1.Text = "Live: ";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(25, 142);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(98, 32);
this.label2.TabIndex = 1;
this.label2.Text = "Dead: ";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(12, 174);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(111, 32);
this.label3.TabIndex = 1;
this.label3.Text = "Status: ";
//
// liveLabel
//
this.liveLabel.AutoSize = true;
this.liveLabel.Location = new System.Drawing.Point(123, 110);
this.liveLabel.Name = "liveLabel";
this.liveLabel.Size = new System.Drawing.Size(0, 32);
this.liveLabel.TabIndex = 1;
//
// deadLabel
//
this.deadLabel.AutoSize = true;
this.deadLabel.Location = new System.Drawing.Point(123, 142);
this.deadLabel.Name = "deadLabel";
this.deadLabel.Size = new System.Drawing.Size(0, 32);
this.deadLabel.TabIndex = 1;
//
// statusLabel
//
this.statusLabel.AutoSize = true;
this.statusLabel.Location = new System.Drawing.Point(123, 174);
this.statusLabel.Name = "statusLabel";
this.statusLabel.Size = new System.Drawing.Size(0, 32);
this.statusLabel.TabIndex = 1;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(16F, 31F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1159, 780);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.statusLabel);
this.Controls.Add(this.deadLabel);
this.Controls.Add(this.liveLabel);
this.Controls.Add(this.label1);
this.Controls.Add(this.button1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label liveLabel;
private System.Windows.Forms.Label deadLabel;
private System.Windows.Forms.Label statusLabel;
}
关于c# - 使用 SemaphoreSlim,在等待所有线程完成时更新 UI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47522590/
情况我想使用 ui-date 在我的应用程序中设置/编辑日期。我使用最新稳定版本的 Angular、Angular-UI、JQuery-UI 等。 问题一旦使用日期选择器选择了日期,我的模型中的日期将
编辑: jQuery UI 可选择小部件内置了一个回调,stop,我需要知道如何以编程方式触发此事件。 (措辞不佳)我已将事件监听器附加到 jQuery UI Selectable Widget 。如
我正在尝试建立一个下一个JS与尾风用户界面提供的反应组件的网络应用程序。顺风用户界面是在幕后使用无头用户界面。。默认情况下,Next JS将构建服务器端组件,除非您在页面顶部添加“使用客户端”。不幸的
我正在尝试建立一个下一个JS与尾风用户界面提供的反应组件的网络应用程序。顺风用户界面是在幕后使用无头用户界面。。默认情况下,Next JS将构建服务器端组件,除非您在页面顶部添加“使用客户端”。不幸的
我正在尝试应用这个 SlickGrid 示例: http://mleibman.github.com/SlickGrid/examples/example4-model.html 到我自己的网络项目。
我想整理我的 Schemas为我的实体类生成,DTO 类位于 Springdoc ui . 我可以对 tags 进行排序和 operations通过以下配置 yml文件,但我的模式不是按排序顺序排列的
有谁知道阻止 ui-sref 重新加载状态的方法吗? 我无法通过“$stateChangeStart”事件执行此操作,因为 ui-sref 仅更改参数而不更改状态名称。 我的左边是书单,左边是书的详细
我正在 jquery ui 对话框中使用 jquery ui 自动完成小部件。当我输入搜索文本时,文本框缩进(ui-autocomplet-loading)但不显示任何建议。 var availabl
我正在尝试将 Kendo UI MVVM 框架与 Kendo UI 拖放机制结合使用;但我很难找到如何将数据从 draggable 对象中删除。 我的代码是这样的...... var viewMode
Kendo UI Web 和 Kendo UI Core 之间有什么区别 https://www.nuget.org/packages/KendoUIWeb http://www.nuget.org/
我正在尝试将 Kendo UI MVVM 框架与 Kendo UI 拖放机制结合使用;但是我很难找到如何从 draggable 对象中删除数据。 我的代码是这样的…… var viewModel =
使用 Angular JS - UI 路由器,我需要从我的父 View project.details 到我的 subview project.details.tasks 进行通信。我的 subvie
KendoUI 版本 2013.3.1119使用 Kendo MVVM 我有一个我构建的颜色选择器,它使用平面颜色选择器和使用调色板的颜色选择器。它们都可以正常运行,但平面颜色选择器的布局已关闭, s
我在非 UI 线程上,我需要创建并显示一个 SaveDialog。但是当我尝试显示它时:.ShowDialog() 我得到: "An unhandled exception of type 'Syst
我正在试验 jquery-ui 并查看和克隆一些示例。在一个示例(自动完成的组合框)中,我看到一个带有 ui-widget 类的 anchor (a) 元素,它与包含的 css 文件中的 .ui-wi
我需要返回一个 UI 列表,我用这个方法: getList(): Observable { return this.httpClient.get("/api/listui").pipe
我有 ui-grids在 angular-ui-tabs ,它们位于 ng-if 中以避免呈现问题。如果有更多数据并且网格进入滚动模式,则单击选项卡时数据会完全消失。我相信这是一个 ui-grids-
这似乎是一个通用的问题,与其他几个 React 开源框架相比,我真的很喜欢 Material ui 的可扩展性。 问题 “@material-ui/core”和“@material-ui/lab”中的
我有一个根页面(index.html),带有侧边栏(“菜单”)和主要内容 div(“主”),因此有两个 ui-view div - 一个称为“菜单”,一个称为“主”。 当主要内容区域有网站列表 (/s
有人在http://jsfiddle.net/hKYWr/上整理了一个很好的 fiddle 。关于使用 angular-ui 和 jqueryui sortable 来获得良好的可排序效果。 如何在两
我是一名优秀的程序员,十分优秀!