C# 如何避免Winform服务总线消息接收器中的跨线程操作无效异常
已经开发了Azure service bus消息接收器控制台应用程序,该应用程序运行良好 控制台应用程序的代码如下:C# 如何避免Winform服务总线消息接收器中的跨线程操作无效异常,c#,winforms,azure,servicebus,ui-thread,C#,Winforms,Azure,Servicebus,Ui Thread,已经开发了Azure service bus消息接收器控制台应用程序,该应用程序运行良好 控制台应用程序的代码如下: using System.IO; using Microsoft.ServiceBus.Messaging; class Program { static void Main(string[] args) { const string connectionString = "Endpoint=sb://sbusnsXXXX.servicebus.
using System.IO;
using Microsoft.ServiceBus.Messaging;
class Program
{
static void Main(string[] args)
{
const string connectionString = "Endpoint=sb://sbusnsXXXX.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=bkjk3Qo5QFoILlnay44ptlukJqncoRUaAfR+KtZp6Vo=";
const string queueName = "bewtstest1";
var queueClient = QueueClient.CreateFromConnectionString(connectionString, queueName);
try
{
queueClient.OnMessage(message => {
string body = new StreamReader(message.GetBody<Stream>(), Encoding.UTF8).ReadToEnd();
Console.WriteLine(body);
message.Complete();
});
Console.ReadLine();
}
catch (Exception ex)
{
queueClient.OnMessage(message => {
Console.WriteLine(ex.ToString());
message.Abandon();
});
Console.ReadLine();
}
}
}
主要形式:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Azure.GetQueue(this);
}
}
代码会编译,但是当收到新的服务总线消息时,我会得到以下异常:
System.InvalidOperationException:'跨线程操作无效:
控件“listBox1”是从其所在线程以外的线程访问的
创建于。”
关于如何避免此异常的想法(注意,我尝试使用invokererequired
,但无法编译代码)
(感觉很接近,因为当我停止并重新运行程序时,表单会加载列表框中的消息,如下所示:!)当然,您不能从另一个线程引用在UI线程中创建的控件;正如您所注意到的,当您尝试执行以下操作时,会引发一个
无效的跨线程操作
异常:Windows窗体应用程序必须是单线程的,文档中详细解释了原因
注意:删除所有控制台.ReadLine()
,您不能在WinForms中使用它(没有控制台)
这里有一些可能适合您的实现,按照与您的上下文相关的顺序排列(好吧,至少我是这么认为的。您可以选择您喜欢的)
► : 这个类的使用非常简单。您只需要定义它的返回类型(T
type,它可以是任何东西,一个简单的string
,一个类对象等等)。您可以就地定义它(在这里调用线程方法)并传递其引用。就这些。接收引用的方法调用其方法,传递由
T
定义的值此方法在创建
Progress
对象的线程中执行。如您所见,您不需要将控件引用传递给
GetQueue()
:
形式方面:
// [...]
var progress = new Progress<string>(msg => listBox1.Items.Add(msg));
Azure.GetQueue(progress);
// [...]
// Add static Field for the SynchronizationContext object
static SynchronizationContext sync = null;
// Add a method that will receive the Post() using an Action delegate
private void Updater(string message) => listBox1.Items.Add(message);
// Call the method from somewhere, passing the current sync context
sync = SynchronizationContext.Current;
Azure.GetQueue(sync, Updater);
// [...]
Azure.GetQueue(this, Updater);
// [...]
// Add a method that will act as the Action delegate
private void Updater(string message) => listBox1.Items.Add(message);
Azure类端:
public static void GetQueue(IProgress<string> update)
{
// [...]
try {
queueClient.OnMessage(message => {
string body = new StreamReader(message.GetBody<Stream>(), Encoding.UTF8).ReadToEnd();
update.Report(body);
message.Complete();
});
}
// [...]
}
public static void GetQueue(SynchronizationContext sync, Action<string> updater)
{
// [...]
try {
queueClient.OnMessage(message => {
string body = new StreamReader(message.GetBody<Stream>(), Encoding.UTF8).ReadToEnd();
sync.Post((spcb) => { updater(body); }, null);
message.Complete();
});
}
// [...]
}
public static void GetQueue(Control control, Action<string> action)
{
// [...]
try {
queueClient.OnMessage(message => {
string body = new StreamReader(message.GetBody<Stream>(), Encoding.UTF8).ReadToEnd();
control.BeginInvoke(new Action(()=> action(body));
message.Complete();
});
}
// [...]
}
Azure类端:
public static void GetQueue(IProgress<string> update)
{
// [...]
try {
queueClient.OnMessage(message => {
string body = new StreamReader(message.GetBody<Stream>(), Encoding.UTF8).ReadToEnd();
update.Report(body);
message.Complete();
});
}
// [...]
}
public static void GetQueue(SynchronizationContext sync, Action<string> updater)
{
// [...]
try {
queueClient.OnMessage(message => {
string body = new StreamReader(message.GetBody<Stream>(), Encoding.UTF8).ReadToEnd();
sync.Post((spcb) => { updater(body); }, null);
message.Complete();
});
}
// [...]
}
public static void GetQueue(Control control, Action<string> action)
{
// [...]
try {
queueClient.OnMessage(message => {
string body = new StreamReader(message.GetBody<Stream>(), Encoding.UTF8).ReadToEnd();
control.BeginInvoke(new Action(()=> action(body));
message.Complete();
});
}
// [...]
}
publicstaticvoidgetqueue(控制、操作)
{
// [...]
试一试{
queueClient.OnMessage(消息=>{
string body=newstreamreader(message.GetBody(),Encoding.UTF8.ReadToEnd();
control.BeginInvoke(新动作(()=>动作(主体));
message.Complete();
});
}
// [...]
}
您还可以使用来管理线程的排队工作项,调用其
BeginInvoke()
(首选)或Invoke()
方法。它的实现类似于
SynchronizationContext
one,其方法称为前面提到的Control.BeginInvoke()
方法
我没有在这里实现它,因为Dispatcher需要对WindowsBase.dll
(通常是WPF)的引用,这可能会在非DpiAware的WinForms应用程序中造成不希望的效果。您可以在这里阅读:
无论如何,如果您感兴趣,请告诉我。谢谢Jimi,非常感谢您的详细回复!!有很多非常好的信息需要查看和消化。我刚刚用上面的代码尝试了进度选项,但在更新(正文)时出现编译错误;GetQueue中的一行写着CS0149方法名称。对此有什么想法吗?谢谢Jimi!效果很好。非常感谢您花这么多时间回答如此全面的问题-为我节省了很多时间和挫折。