当前位置: 首页 > 知识库问答 >
问题:

Xamarin Android-task.run vs task.factory.startNew和Thread.CurrentPrincipal

宣俊豪
2023-03-14

我有一个Xamarin.Android项目,TargetFramework=Android版本:8.0(奥利奥)。

我注意到以下问题:

  1. Thread.CurrentPrincipal设置为自定义主体(要格外小心,确保它是可序列化的!)
  2. 调用Await Task.run()来运行某些任务。
  3. 任务中,如果它在其他线程上执行,则不会设置Thread.CurrentPrincipal。
  4. 如果使用Task.Factory.StartNew()而不是Task.Run(),则在后台线程上运行的任务确实正确设置了thread.CurrentPrincipal。

换句话说,Task.Factory.StartNew似乎将CurrentPrinciple传递给新线程,而在新线程中,Task.Run没有

此外,如果在Net4.7上重复此测试,Thread.CurrentPrincipal在两种情况下都能正确流动,因此这种行为差异仅在运行在Mono/Xamarin.Android上时才会出现。

下面是一个测试用例:

    [Fact]
    html" target="_blank">public async Task LoginService_WhenUserLogsIn_PrincipalFlowsToAsyncTask()
    {    

        var mockIdentity = new MockIdentity(true);
        var mockPrincipal = new MockPrincipal(mockIdentity);          
        Thread.CurrentPrincipal = mockPrincipal ;          

        await Task.Factory.StartNew(async () =>
        {
            var newThreadId = Thread.CurrentThread.ManagedThreadId; // on different thread.
            Assert.True(Thread.CurrentPrincipal.Identity.IsAuthenticated);
            Assert.Equal(mockPrincipal, Thread.CurrentPrincipal);

            await Task.Factory.StartNew(() =>
            {
                 // still works even when nesting..
                newThreadId = Thread.CurrentThread.ManagedThreadId;
                Assert.True(Thread.CurrentPrincipal.Identity.IsAuthenticated);
                Assert.Equal(mockPrincipal, Thread.CurrentPrincipal);

            }, TaskCreationOptions.LongRunning);

        }, TaskCreationOptions.LongRunning);

        await Task.Run(() =>
        {
           // Following works on NET4.7 and fails under Xamarin.Android.
            var newThreadId = Thread.CurrentThread.ManagedThreadId;
            Assert.True(Thread.CurrentPrincipal.Identity.IsAuthenticated);
            Assert.Equal(mockPrincipal, Thread.CurrentPrincipal);

        });

    }
    null

这是StartNew()之前的情况:

以下是startnew()中的内容:

请注意,通过StartNew()调度的任务正在线程池线程上执行,并且ShcnronisationContext TaskScheduler为空,因为此时没有当前同步上下文。但是,还要注意,线程的主体被正确地设置为“DAZ”标识。

然而,SyncContext TaskScheduler似乎已经更改(它的ID现在是3)。不确定这是否与这个问题有关。

下面是task.run()过程中的情况:

注意:thread.CurrentPrincipal.IdentityName已丢失(即因为Prinicpal没有像StartNew()那样流到此线程。

我们仍然处于一个后台线程上,就像我们使用StartNew()时一样。这个线程上没有SynchronisationContext,这是我们在后台线程中所期望的,并且在StartNew()中是相同的,所以没有区别。默认任务调度器和当前任务调度器看起来是相同的(两个ID都=1),因此也没有区别。

原因何在?这是窃听器吗?我以为在使用task.run()时,Thread.CurrentPrinicpal应该始终与ExecutionContext一起流动。

我还在这里提出了Github上的一个问题:https://Github.com/xamarin/xamarin-android/issues/1130

更新:看起来这是一个错误,希望通过https://github.com/mono/mono/pull/6326/files修复

共有1个答案

袁英豪
2023-03-14

编辑开始:

我会留下我的答案,尽管任何人读到这一点应该注意,它与问题无关,我不介意它挂在未回答,但请不要标记它。它下面有可能相关的通信。

我仔细看了一下这个问题,意识到被问到了什么,并且可以诚实地同意我觉得这可能是一个bug。我无法解释它,我觉得这是很好的知道前进。我将亲自关注这个问题以寻求解决办法。

编辑结束:

我不完全明白你在做什么。用断点或者悬停在变量上?将这些变量放在两个任务之外,然后在它们运行之后再查看它们。

但我想这是你要找的答案。当输入Foo时,它只是一个带有任务返回类型的异步方法。是的,这意味着我们可以在调用时等待它,但在它实际运行在一个新线程上之前,什么也不会等待。因此,在Foo到达await task.factory.startnew之前,您将保留在调用await Foo()的任何上下文中。

此外,您还可以在Await调用的末尾使用.configureawait(false);,但这不会返回到调用上下文。它将安排线程继续运行在任何最优的时间,这可能会导致问题,如果您不知道。总体而言,它更有效,但要确保html" target="_blank">方法的其余部分不需要在相同的上下文中进行操作。

希望这有帮助。

 类似资料:
  • 问题内容: 关于它们有很多传说。我想知道真相。以下两个示例之间有什么区别? 问题答案: 不确定从何处获得传说,但: 提交按钮 与: IE6将在标记之间提交此按钮的所有文本,其他浏览器将仅提交值。使用可使您在按钮的设计上享有更大的布局自由度。从各种意图和目的看,它乍一看似乎很棒,但是各种浏览器怪癖使它有时很难使用。 在您的示例中,IE6将发送到服务器,而其他大多数浏览器将不发送任何内容。要使其跨浏览

  • 什么区别以及如何正确重写代码?

  • 我试图理解为什么下面两个代码块会产生不同的结果。 代码块1按预期工作,并返回从数据库中查找的提供程序的数组。另一方面,代码块2返回函数数组。在理解promissione.all()和async/await时,我觉得缺少了一些简单的东西。 代码块的差异如下: > 块1:创建许诺函数数组,然后使用map运算符将其包装在异步函数中。 块2:许诺函数的数组被创建为异步函数。因此,不调用map运算符。 如果

  • 问题内容: 我才刚刚开始研究SQL。 我有一个SQL Server 2008r2数据库,它将返回两个字段DocDate和InvValue。我需要将InvValues汇总为今天的MTD和YTD,所以看起来像 我已经做了大量的Google搜寻,并且可以使用SUM&DATEPART进行一项或多项,但是我坚持尝试两者兼而有之。 有人可以给我一些伪代码,以帮助我进一步谷歌。 谢谢@戈登·利诺夫(Gordon

  • 这个示例代码来自一本Java书籍,从墙上的99瓶啤酒到没有啤酒打印出这首歌。问题是,当墙上是1瓶啤酒时,它仍然写着瓶子。我试图通过在末尾添加部分来解决这个问题。但是,它仍然显示1瓶啤酒在墙上,我瓶啤酒在墙上。 我不知道该改变什么来解决这个问题。是否创建另一个while部分? 如果你能给他们一个提示,这样我就可以自己解决它,那也很酷!因为我知道我的实际歌曲输出是在第一个if部分,但我不知道我应该在哪

  • 原文地址:https://cesiumjs.org/tutorials/cesium-and-webpack/ Cesium 和 Webpack Webpack是非常强大非常流行的JavaScript 模块打包工具。它可以让开发人员以一种简单直观的 require 方式去加载各种页面需要的文件,极大的方便了开源人员对代码和资源文件进行结构化设计。当编译的时候,它会跟踪代码依赖性,把所有的模型打包到

  • 问题内容: 我一直在学习redis和node.js。我有两个问题,找不到令人满意的答案。 我的第一个问题是关于在node.js中重用Redis客户端。我找到了这个问题和答案:如何在socket.io中重用Redis连接,但还不足以让我满意。 现在,如果我在连接事件中创建redis客户端,它将为每个连接生成。因此,如果我有2万个并发用户,那么将有2万个Redis客户端。 如果我将其放在连接事件之外,

  • 我需要控制日志框架的最大磁盘空间量。 例如,在log4j中,如果有如下所示的追加器,我可以很容易地估计需要多少磁盘空间: 对于maxFileSize和maxBackupIndex,我知道最多需要10x100KB。 我注意到DailyRollingFileAppender,但它不支持maxFileSize。 除了RollingFileAppender之外,是否还有其他fileappender可以实现