110,500
社区成员
发帖
与我相关
我的任务
分享
interface 平台接口
{
所有平台 平台 { get; } //枚举类型
Double[,] 余额 { get; }
Double[,] 委买 { get; set; }
Double[,] 委卖 { get; set; }
event Action<所有通知, 所有平台, Byte> 更新;
void 启动();
Task<Double> 计算成交量(交易方向 方向, Double 价格, Double 数量);
}
这几天讨论的焦点都围绕这个Task<Double> 计算成交量(交易方向 方向, Double 价格, Double 数量);struct okex : 平台接口
{
//此处省略100字
String 单号;
Double 成交量;
Task 延时任务;
public okex(Byte 位置, 所有代币[] 交易对)
{
//此处省略100字
单号 = String.Empty;
成交量 = 0;
延时任务 = null;
}
public async void 启动()
{
客户端 = new ClientWebSocket();
await 客户端.ConnectAsync(new Uri("wss://real.okex.com:10441/websocket"), CancellationToken.None);
监听();
订阅行情();
登陆();
账户信息();
定时器响应 = new Timer(检查状态, null, 29000, 30000);
}
void 检查状态(object 参数) => 发送("{'event':'ping'}");
async void 监听()
{
String 缓存 = String.Empty;
SByte 累计差 = 0;
const UInt16 最大字节数 = 4092; //超出后将拆分
var 缓冲 = new byte[最大字节数 * 2]; //服务端最大4092
while (客户端.State == WebSocketState.Open)
{
var 字节 = await 客户端.ReceiveAsync(new ArraySegment<byte>(缓冲), CancellationToken.None);
String 收到 = Encoding.Default.GetString(缓冲, 0, 字节.Count);
解析(Datetime.Now, 收到);
}
}
void 解析(DateTime 时间, String 消息)
{
if (消息 == "{\"event\":\"pong\"}") //服务器响应心跳包
心跳时间 = DateTime.Now;
else
{
foreach (JObject 成员 in JArray.Parse(消息)) //遍历消息组
{
if (成员["channel"].ToString() == 表达式行情深度 && 方法.时间戳转时间(成员["data"]["timestamp"].ToString()) > 行情时间) //更新行情
{
行情时间 = 方法.时间戳转时间(成员["data"]["timestamp"].ToString());
更新(所有通知.行情, 平台, 位置);
}
else if (成员["channel"].ToString() == 表达式成交通知) //交易通知
{
if ((SByte)成员["data"]["status"] == -1) //撤单后的成交明细
{
成交量 = (Double)成员["data"]["completedTradeAmount"];
//回复运行等待任务
}
}
else if (成员["channel"].ToString() == "ok_spot_order") //下单事件
单号 = 成员["data"]["order_id"].ToString();
else if (成员["channel"].ToString() == "ok_spot_cancel_order") //撤单事件
{}
else if (成员["channel"].ToString() == "login") //登陆事件
{}
else if (成员["channel"].ToString() == "ok_spot_userinfo") //查询余额
{
if ((bool)成员["data"]["result"])
更新(所有通知.余额, 平台, 位置);
}
}
}
}
public void 订阅行情()
{
发送($"{{'event':'addChannel','channel':'{表达式行情深度}'}}");
}
public void 登陆()
{
发送("{'event':'login','parameters':{'api_key':'" + 大 + "','sign':'" + 参数签名(new String[1], 0) + "'}}");
}
public void 账户信息()
{
发送("{'event':'addChannel','channel':'ok_spot_userinfo','parameters':{'api_key':'" + 大 + "','sign':'" + 参数签名(new String[1], 0) + "'}}");
}
void 撤单()
{
发送($"{{'event':'addChannel','channel':'ok_spot_cancel_order','parameters':{{'api_key':'{大}','sign':'{密文}','symbol':'{表达式交易对}','order_id':'{单号}'}}}}");
}
async void 发送(string 指令)
{
await 客户端.SendAsync(new ArraySegment<byte>(Encoding.Default.GetBytes(指令)), WebSocketMessageType.Text, true, new CancellationToken());
}
public async Task<double> 计算成交量(交易方向 方向, double 价格, double 数量)
{
发送($"{{'event':'addChannel','channel':'ok_spot_order','parameters':{{'api_key':'{大}','sign':'{密文}','symbol':'{表达式交易对}','type':'{ (方向 == 交易方向.买 ? "buy" : "sell")}','price':{价格},'amount':{数量}}}}}");
延时任务 = new Task(等待);
await new Task(等待); //卡死
return 成交量;
}
void 等待() => 延时任务.Wait();
}
WebStocket不是一问一答机制的,当程序运行起来后,服务器就不停想我发送msg了。为了可以被主程序调用,我要封装一个async Task<Double> 计算成交量(交易方向 方向, Double 价格, Double 数量);private void 测试按钮_Click(object sender, EventArgs e)
{
okex 币行 = new okex(参数);
消息框.Text = 币行.测试任务().Result.ToString();
}
程序卡死在代码的第97行上,这行上有await,搞了那么多天了,把内容说清楚,讲明白。public partial class 测试 : Form
{
public 测试() => InitializeComponent();
static String 成交量 = "已算出";
Task<String> 回调 = new Task<String>(() => 成交量);
async void 显示() => label1.Text = await 回调;
private void button1_Click(object sender, EventArgs e) => 显示();
private void button2_Click(object sender, EventArgs e) => 回调.Start();
}
public async Task<double> 计算成交量(交易方向 方向, double 价格, double 数量)
{
发送($"{{'event':'addChannel','channel':'ok_spot_order','parameters':{{'api_key':'{大}','sign':'{密文}','symbol':'{表达式交易对}','type':'{ (方向 == 交易方向.买 ? "buy" : "sell")}','price':{价格},'amount':{数量}}}}}");
延时任务 = new Task(等待);
await new Task(等待); //卡死
return 成交量;
}
这里的“return 成交量”纯粹是一个胡乱拼凑来的“全局变量”概念,而不是 async/await 机制。
真正的 async/await 代码是形如这样的代码 public async Task<double> 计算成交量(交易方向 方向, double 价格, double 数量)
{
var id = 获得消息唯一编号();
发送($"{{'event':'addChannel','channel':'ok_spot_order','parameters':{{'api_key':'{大}','sign':'{密文}','symbol':'{表达式交易对}','type':'{ (方向 == 交易方向.买 ? "buy" : "sell")}','price':{价格},'amount':{数量}}}}}", id);
var result = await 消息返回结果(id);
return result;
}
这种代码。这种代码就算是如你问题标题所说“模仿同步代码”,它也是学过语法形式才有根有据地写出来的代码。假设你用一个无根据的“成交量”变量来作为返回,那肯定要整天在那里纠结了。 return await session.callback;
这条语句执行之前,其实没有任何线程在那里死等。如果纠结什么“阻塞、waitone、(你的)等待”,这就太不实际了。根本没有那些多余的代码!你可以通过调试看到,当lst[0].callback.Start();
被执行之后立刻触发了return await session.callback;
代码,这里的关键是异步回调的基本概念。
这就好像是把用头走路改为用脚走路,或者犹如分清楚镜子里的你和真实的自己,你的程序之所以“死掉”是因为走火入魔地完全用同步顺序执行的脑袋来理解异步设计,而且无法开窍。s.Result=.....;
s.callback.Start()
或者s.callback(....)
这样的方法回调。
发送信息时可以发送很多命令,收到返回信息的顺序跟发送命令完全可能不同。
这里如果抱着死板地“等待、阻塞、顺序处理”的思路来理解异步,那就只能靠悟性了。 static async Task<string> SendMessage(byte[] message)
{
var session = new Session
{
//......
};
session.callback = new Task<string>(() => session.Result);
lst.Add(session);
sendmessage(message);
return await session.callback;
}
这样的代码时,应该清楚地能够说出,await 之前的代码是这个方法主体要执行的代码,而 await 后边(右边和下边)的代码是封装到 Task<string> 类型对象里边去的委托,并不会立刻被执行。
切不可把 await 理解为什么阻塞、信号量之类的概念。