我正在处理一个托管对象在async方法中间过早完成的情况.
这是一个业余爱好家庭自动化项目(Windows 8.1,.NET 4.5.1),我向非托管第三方DLL提供C#回调.在某个传感器事件时调用回调.
为了处理这个事件,我使用async/await了一个简单的自定义awaiter(而不是TaskCompletionSource).我这样做的部分原因是为了减少不必要的分配数量,但主要是出于好奇心作为学习练习.
下面是我所拥有的非常剥离的版本,使用Win32计时器队列计时器来模拟非托管事件源.让我们从输出开始:
Press Enter to exit... Awaiter() tick: 0 tick: 1 ~Awaiter() tick: 2 tick: 3 tick: 4
请注意我的等待者在第二次打勾后如何最终确定.这是出乎意料的.
代码(控制台应用程序):
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
class Program
{
static async Task TestAsync()
{
var awaiter = new Awaiter();
//var hold = GCHandle.Alloc(awaiter);
WaitOrTimerCallbackProc callback = (a, b) =>
awaiter.Continue();
IntPtr timerHandle;
if (!CreateTimerQueueTimer(out timerHandle,
IntPtr.Zero,
callback,
IntPtr.Zero, 500, 500, 0))
throw new System.ComponentModel.Win32Exception( …Run Code Online (Sandbox Code Playgroud) .net c# garbage-collection task-parallel-library async-await
使用新的async/await模型,生成一个Task在事件触发时完成的模型非常简单; 你只需要遵循这种模式:
public class MyClass
{
public event Action OnCompletion;
}
public static Task FromEvent(MyClass obj)
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
obj.OnCompletion += () =>
{
tcs.SetResult(null);
};
return tcs.Task;
}
Run Code Online (Sandbox Code Playgroud)
这允许:
await FromEvent(new MyClass());
Run Code Online (Sandbox Code Playgroud)
问题是你需要为FromEvent你想要的每个类中的每个事件创建一个新方法await.这可能会变得非常快,而且无论如何它主要只是样板代码.
理想情况下,我希望能够做到这样的事情:
await FromEvent(new MyClass().OnCompletion);
Run Code Online (Sandbox Code Playgroud)
然后我可以FromEvent对任何实例上的任何事件重复使用相同的方法.我花了一些时间来尝试创建这样的方法,并且存在许多障碍.对于上面的代码,它将生成以下错误:
事件'Namespace.MyClass.OnCompletion'只能出现在+ =或 - =的左侧
据我所知,没有办法通过代码传递这样的事件.
所以,下一个最好的事情似乎是尝试将事件名称作为字符串传递:
await FromEvent(new MyClass(), "OnCompletion");
Run Code Online (Sandbox Code Playgroud)
它并不理想; 如果该类型的事件不存在,你就不会得到intellisense并且会遇到运行时错误,但它仍然比大量的FromEvent方法更有用.
因此,使用反射和GetEvent(eventName)获取EventInfo对象很容易.下一个问题是该事件的委托在运行时是未知的(并且需要能够变化).这使得添加事件处理程序变得困难,因为我们需要在运行时动态创建方法,匹配给定签名(但忽略所有参数),访问TaskCompletionSource我们已有的并设置其结果.
幸运的是,我发现此链接包含有关如何[几乎]完全通过的说明Reflection.Emit.现在的问题是我们需要发出IL,我不知道如何访问tcs我拥有的实例.
以下是我为完成此任务所取得的进展:
public static Task …Run Code Online (Sandbox Code Playgroud) 目前,根据我的研究,有三种方法可以异步使用套接字:
.Net 4.5异步示例:使用.Net 4.5异步功能进行套接字编程(第二篇文章)
[...] Async:http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx
开始[...]:http://msdn.microsoft.com/en-us/library/5w7b7x5f(v = vs.110).aspx
我对所有选项非常困惑.Net提供了使用异步套接字的方法.我为什么要使用其中一个?有数千个同时连接的性能有什么更好的选择?
有人能指出我使用.net 4.5异步API(异步,等待,任务<>,ReadAsync等)进行串行通信的工作示例吗?
我试图调整现有的事件驱动的串行样本,并且得到各种可怕的行为 - "其他应用程序使用的端口"错误,VS2013调试器抛出异常和锁定 - 这通常需要PC重启才能从中恢复.
编辑
我从头开始编写自己的样本.这是一个简单的Winforms项目,它写入Output窗口.表单上的三个按钮 - 打开端口,关闭端口和读取数据.ReadDataAsync方法调用SerialPort.BaseStream.ReadAsync.
截至目前,它将从端口读取数据,但我遇到了使其变得强大的问题.
例如,如果我拔下串行电缆,打开端口,然后单击Read Data两次,我将得到一个System.IO.IOException(我有点期待),但我的应用程序停止响应.
更糟糕的是,当我尝试停止我的程序时,VS2013会抛出一个"正在进行停止调试"对话框,该对话框永远不会完成,我甚至无法从任务管理器中删除VS. 每次发生这种情况都必须重启我的电脑.
不好.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private SerialPort _serialPort;
public Form1()
{
InitializeComponent();
}
private void openPortbutton_Click(object sender, EventArgs e)
{
try
{
if (_serialPort == null )
_serialPort = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One);
if (!_serialPort.IsOpen)
_serialPort.Open(); …Run Code Online (Sandbox Code Playgroud) 我有很多像这样的代码:
var feed = new DataFeed(host, port);
feed.OnConnected += (conn) =>
{
feed.BeginLogin(user, pass);
};
feed.OnReady += (f) =>
{
//Now I'm ready to do stuff.
};
feed.BeginConnect();
Run Code Online (Sandbox Code Playgroud)
如您所见,我使用通常的异步操作方式.如何更改此代码才能使用async await?最好是这样的:
public async void InitConnection()
{
await feed.BeginConnect();
await feed.BeginLogin(user, pass);
//Now I'm ready
}
Run Code Online (Sandbox Code Playgroud) 嗨伙计们我想把第三方功能包装到Task就可以等待完成回调功能了.这就是我想要实现的目标..
public MyClass MyProperty {
get
{
if (myProperty == null)
myProperty = LoadMyValue("1234");
return myProperty ;
}
set
{
myProperty = value;
}
}
public MyClass LoadMyValue(string id) {
return MyClassTools.LoadMyValue(id);
}
Run Code Online (Sandbox Code Playgroud)
在MyClassTools中
static public MyClass LoadMyValue(string id) {
3rdPartyApi.Call(id, Callback);
// here I want to return Callback result
}
static MyClass Callback(3rdPartyResult result) {
return new MyClass(result);
}
Run Code Online (Sandbox Code Playgroud)
有没有人知道如何在这里使用Tasks和async/await能够直接从函数LoadMyValue返回Callback结果?
async-await ×5
c# ×5
.net ×3
asynchronous ×2
callback ×1
cil ×1
performance ×1
serial-port ×1
sockets ×1
task ×1