自定义异步方法:
public static async Task TransactionTestAsync() { Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId); int res = await AsyncTest(); //自定义的异步方法 Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId); } public static async Task<int> AsyncTest() { return await Task.Run(() => { return 1; }); }
打印出来的线程前后基本是不一致的
下面使用dapper的异步方法:
public static async Task TransactionTestAsync() { Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId); string sql1 = "INSERT INTO `person` (`Name`, `Sex`, `Age`) VALUES ('test', '1', '25');select @@IDENTITY;"; int res = await MySqlHelper.ExecuteScalarAsync<int>(sql1); //调用Dapper里面的异步方法 Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId); }
重复运行上面代码,当使用dapper的异步方法时打印出来的线程ID总是一样
按照我的理解来说使用await后面的代码都相当于是回调,那么应该使用与主线程不一样的线程,也就是前后线程不一致,那么像dapper提供的异步方法是怎么实现现在与主线程一样的呢?
MySqlHelper.ExecuteScalarAsync
方法中要加上 async/await
试过了,加和不加其实效果一样
public static async Task<T> ExecuteScalarAsync<T>(string sql, object param = null, string connStr = null, int? commandTimeout = null)
{
using (var conn = GetDbConnection(connStr))
{
return await conn.ExecuteScalarAsync<T>(sql, param, commandTimeout: commandTimeout, commandType: CommandType.Text);
}
}
@爱杨洋真是太好了: 再试试
@dudu: 试了100次,全部都是一样的,正常来讲await后面的代码相当于是回调,基本是不可能与主线程一样的
@dudu: .net core 2.2下的控制台调试的
@爱杨洋真是太好了: 难怪,控制台程序就一个主线程,换成 web 程序
@dudu: 使用web
public async Task<IActionResult> Index() { Person person = new Person() { Name = "侯哥", Age = 25, Sex = 1 }; var logger= _loggerFactory.CreateLogger<HomeController>(); logger.LogInformation(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString("00")); string sql = @"INSERT INTO `Person` (`Name`, `Sex`, `Age`) VALUES (@Name, @Sex, @Age); "; int a= await MySqlHelper.ExecuteAsync(sql, person); logger.LogInformation(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString("00")); logger.LogInformation("************************************"); return View(); }
试了很多次,结果是这样
@dudu: 还有个问题,就是如果2个线程不一样的话,我理解的是request是基于当前线程的,那么await后面的代码是不是就拿不到request信息了呢?
@爱杨洋真是太好了: await 是释放当前线程,异步执行完成后从线程池中取一个线程继续执行,如果没有什么并发,线程池中的线程很少,很可能从线程次拿到的也是之前释放的线程
@dudu: 我也是这样理解的,await后面的代码相当于是一个回调,内部是一个状态机的实现方式。
现在我不使用dapper提供的异步方法,我自定义一个异步的方法,如下
public async Task<IActionResult> Index() { object o1 = Request; var logger= _loggerFactory.CreateLogger<HomeController>(); logger.LogInformation(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString("00")); int a =await AsyncTest();//自定义的异步方法 logger.LogInformation(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString("00")); object o2 = Request; Console.WriteLine(object.ReferenceEquals(o1,o2));//打印出来的true return View(); } public static async Task<int> AsyncTest() { return await Task.Run(() => { return 1; }); }
现在前后的线程ID是不样的,这个可以说的通,因为是await后面的代码是从线程池中重新取的一个线程,那么现在2个request竟然是同一个,这个是框架本身同步的吗?
但是我使用dapper框架的异步方法就会导致前后线程是一样的,这个就很难讲的通,dapper的方法返回的也是一个Task<int>,难道dapper内部的实现不是通过task实现的吗?
@爱杨洋真是太好了: 能否提供一下重现这个问题的示例代码,我明天找时间测试一下
@dudu: 可以,代码怎么发你
@爱杨洋真是太好了: 上传到 github
@dudu: 地址:https://github.com/houwencheng1993/KTSCRMApi
直接运行,就可以,代码在KTSCRM.Admin home index下面,使用dapper时需要访问数据库
@爱杨洋真是太好了: 是线程池线程太少的原因,可以在 Startup.Configure 中添加下面的代码增加线程数
if (ThreadPool.SetMinThreads(10, 10))
{
Parallel.For(0, 10, a => Thread.Sleep(1000));
}
@dudu: 我是.net core 2.2的版本,设置 SetMinThreads(50, 50),await前后线程打印出来还是一样,我怀疑与dapper的异步方法的实现有关系
前后是否同一线程取决于Async方法内部。
通常来说,调用真正的异步,方法的前后线程是不一样的。
但是,有些情况下,异步是假的。比如
public static async Task<int> AsyncTest(int x)
{
if (x == 0) return 0; // 假异步,同步执行
if (x == 1) return await Task.FromResult<int>(0);//假异步,同步执行
return await Task.Run(() => x);//真异步
}
那真异步下await前后如何
保证request相同呢
@爱杨洋真是太好了:
使用阻塞的方式,如:
Task<int> t= AsyncTest(123);
//do something
var result=t.Result; //或 t.Wait();
假异步下确实线程是相同的,关于request的问题是说现在await前后线程确实不一样了,但是打印出来的request是相同的,感觉这个是框架本身做了什么
这个应该是利用Oracle驱动程序(ODP.NET)的方法不是真正的异步方法,里面是阻塞的,所以每次前后的线程ID都是一样的
建议提供一下 MySqlHelper.ExecuteScalarAsync 部分的代码
– dudu 5年前@dudu: public static Task<T> ExecuteScalarAsync<T>(string sql, object param = null, string connStr = null, int? commandTimeout = null)
– 爱杨洋真是太好了 5年前{
using (var conn = GetDbConnection(connStr))
{
return conn.ExecuteScalarAsync<T>(sql, param, commandTimeout: commandTimeout, commandType: CommandType.Text);
}
}
@dudu: 其实就是调用了dapper类库提供的ExecuteScalarAsync方法
– 爱杨洋真是太好了 5年前