如何进行C#异步编程?
How to do C# asynchronous programming?
我正在使用我很久以前用 Visual Basic.net 编写的数据库导出工具。
我切换到 C# 并喜欢用 C# 重新编程,因为我积累了比以前更多的经验:-)
我的 C# 应用程序 UI 挂起,因为有一个大的数据库查询要做。所以我了解了异步编程。也尝试过线程和任务,但我无法找到适合我的问题的方法。
这就是我所拥有的:它是一个 Windows 表单应用程序,并且有一个创建新线程的开始按钮。
名为 Export 的方法是我在名为 actions.cs
的第二个 class 文件中创建的非静态方法。它是非静态的,因为该方法应该在代码中经常重复使用,但参数不同。
所以我在Form1的Button_Clicked
事件中用相应的参数实例化了方法:
actions KuliMon = new actions()
{
ExportPath = settings.ReadActSetting("baexport", "ExportPfad", ""),
Trennzeichen = settings.ReadGlobSetting("Trennzeichen", ";"),
ConnectionString = settings.ReadGlobSetting("Mand1_odbc", ""),
SQLFile = "kuli.sql",
ExportAktion = "kuli"
};
然后我从 Button_click 事件开始了这样的话题:
Thread ExportThread = new Thread(KuliMon.Export);
ExportThread.Start();
这行得通。不粘 GUI。但是现在问题来了。我在 actions.cs 中的导出方法正在将 DB-Query 导出到一个 csv 文件中,但也应该 return 将结果放入一个字符串变量中,然后我可以在 Form1 的文本框中显示它。
通过阅读一些资料,我发现 Invoke-Method 对我有很大帮助。在 Thread.Start() 下,我添加了以下内容:
this.Invoke((MethodInvoker)delegate
{
tx_main_t1.Text = "Hello";
});
当我单击按钮时,文本框显示 "hello"。但是我需要线程中的方法 Export 运行 中的 Return 字符串而不是 hello。问题是如何获取包含查询结果的字符串。
根据我的理解,线程方法必须调用一个 void 方法并且不能 return 一个值。
我想创建一个 public 属性 字符串并在导出中用 return 值填充该字符串,如下所示:
public string results { get; set; }
我试过
而不是在方法导出中使用 return ReturnValue
results = ReturnValue;
然后在 Form1 中,我尝试用 KuliMon.results 填充文本框,但它是空的,因为我已经按照我的想法创建了一个 Export 实例。
您应该查看 BackGroundWorker class
https://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx
您可以指定一个函数在工作人员完成工作时调用,并让该函数更新您的 UI。
使用 async await supported in .Net 4.5 and c#5 (you can get support for this in erarlier .Net versions with AsyncBridge package for 3.5 or for 4.0)
private async void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
try
{
//Code will wait here for your method to return without blocking UI Exceptions or Result will be automatically Scheduled to UI thread
string result = await DoStuffAsync("myParameter");
}
catch
{
MessageBox.Show("Ups an error");
}
finally
{
button1.Enabled = true;
}
}
/// <summary>
/// Wraps the synchron implementation off DoStuff
/// </summary>
public Task<string> DoStuffAsync(string s)
{
return Task.Factory.StartNew(DoStuff, s); //here parameter s is forwarded to your synchronus implementation
}
/// <summary>
/// Your orginal synchron implementation with the dbQuerry
/// or long running calculations
/// </summary>
public string DoStuff(string s)
{
//do your normal code;
return result
}
数据库查询和写入文件都是 I/O-bound 操作,因此它们很适合 async
和 await
。
首先,您将定义 KuliMon.Export
的异步版本:
async Task<string> ExportAsync()
{
var data = await myQuery.ToListAsync();
await myStream.WriteAsync(...);
return results;
}
例如,您可以使用 Entity Framework 6 for asynchronous query support。
然后您可以从您的 UI 中这样调用它:
async void button1_Clicked(...)
{
TextBox1.Text = await myInstance.ExportAsync();
}
如果您出于某种原因不能使用异步数据库查询(例如,我认为 Oracle 目前不支持它们),那么您可以使用调用同步 API 的后台线程。请注意 Task.Run
是 Thread
和 BackgroundWorker
的现代替代品:
string Export();
...
async void button1_Clicked(...)
{
TextBox1.Text = await Task.Run(() => myInstance.Export());
}
我正在使用我很久以前用 Visual Basic.net 编写的数据库导出工具。
我切换到 C# 并喜欢用 C# 重新编程,因为我积累了比以前更多的经验:-)
我的 C# 应用程序 UI 挂起,因为有一个大的数据库查询要做。所以我了解了异步编程。也尝试过线程和任务,但我无法找到适合我的问题的方法。
这就是我所拥有的:它是一个 Windows 表单应用程序,并且有一个创建新线程的开始按钮。
名为 Export 的方法是我在名为 actions.cs
的第二个 class 文件中创建的非静态方法。它是非静态的,因为该方法应该在代码中经常重复使用,但参数不同。
所以我在Form1的Button_Clicked
事件中用相应的参数实例化了方法:
actions KuliMon = new actions()
{
ExportPath = settings.ReadActSetting("baexport", "ExportPfad", ""),
Trennzeichen = settings.ReadGlobSetting("Trennzeichen", ";"),
ConnectionString = settings.ReadGlobSetting("Mand1_odbc", ""),
SQLFile = "kuli.sql",
ExportAktion = "kuli"
};
然后我从 Button_click 事件开始了这样的话题:
Thread ExportThread = new Thread(KuliMon.Export);
ExportThread.Start();
这行得通。不粘 GUI。但是现在问题来了。我在 actions.cs 中的导出方法正在将 DB-Query 导出到一个 csv 文件中,但也应该 return 将结果放入一个字符串变量中,然后我可以在 Form1 的文本框中显示它。
通过阅读一些资料,我发现 Invoke-Method 对我有很大帮助。在 Thread.Start() 下,我添加了以下内容:
this.Invoke((MethodInvoker)delegate
{
tx_main_t1.Text = "Hello";
});
当我单击按钮时,文本框显示 "hello"。但是我需要线程中的方法 Export 运行 中的 Return 字符串而不是 hello。问题是如何获取包含查询结果的字符串。
根据我的理解,线程方法必须调用一个 void 方法并且不能 return 一个值。
我想创建一个 public 属性 字符串并在导出中用 return 值填充该字符串,如下所示:
public string results { get; set; }
我试过
而不是在方法导出中使用return ReturnValue
results = ReturnValue;
然后在 Form1 中,我尝试用 KuliMon.results 填充文本框,但它是空的,因为我已经按照我的想法创建了一个 Export 实例。
您应该查看 BackGroundWorker class
https://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx
您可以指定一个函数在工作人员完成工作时调用,并让该函数更新您的 UI。
使用 async await supported in .Net 4.5 and c#5 (you can get support for this in erarlier .Net versions with AsyncBridge package for 3.5 or for 4.0)
private async void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
try
{
//Code will wait here for your method to return without blocking UI Exceptions or Result will be automatically Scheduled to UI thread
string result = await DoStuffAsync("myParameter");
}
catch
{
MessageBox.Show("Ups an error");
}
finally
{
button1.Enabled = true;
}
}
/// <summary>
/// Wraps the synchron implementation off DoStuff
/// </summary>
public Task<string> DoStuffAsync(string s)
{
return Task.Factory.StartNew(DoStuff, s); //here parameter s is forwarded to your synchronus implementation
}
/// <summary>
/// Your orginal synchron implementation with the dbQuerry
/// or long running calculations
/// </summary>
public string DoStuff(string s)
{
//do your normal code;
return result
}
数据库查询和写入文件都是 I/O-bound 操作,因此它们很适合 async
和 await
。
首先,您将定义 KuliMon.Export
的异步版本:
async Task<string> ExportAsync()
{
var data = await myQuery.ToListAsync();
await myStream.WriteAsync(...);
return results;
}
例如,您可以使用 Entity Framework 6 for asynchronous query support。
然后您可以从您的 UI 中这样调用它:
async void button1_Clicked(...)
{
TextBox1.Text = await myInstance.ExportAsync();
}
如果您出于某种原因不能使用异步数据库查询(例如,我认为 Oracle 目前不支持它们),那么您可以使用调用同步 API 的后台线程。请注意 Task.Run
是 Thread
和 BackgroundWorker
的现代替代品:
string Export();
...
async void button1_Clicked(...)
{
TextBox1.Text = await Task.Run(() => myInstance.Export());
}