我有一种情况,对 CancellationTokenSource.Cancel 的调用永远不会返回。相反,在 Cancel 被调用之后(在它返回之前),执行将继续执行被取消代码的取消代码。如果被取消的代码随后没有调用任何可等待的代码,那么最初调用 Cancel 的调用者永远不会取回控制权。这很奇怪。我希望 Cancel 简单地记录取消请求并立即返回独立于取消本身。事实上,调用 Cancel 的线程最终会执行属于被取消操作的代码,并且在返回给 Cancel 的调用者之前这样做看起来像框架中的错误。
这是怎么回事:
有一段代码,我们称之为“工作代码”,它正在等待一些异步代码。为简单起见,假设此代码正在等待 Task.Delay:
try
{
await Task.Delay(5000, cancellationToken);
// …
}
catch (OperationCanceledException)
{
// ….
}
就在“工作代码”调用 Task.Delay 之前,它正在线程 T1 上执行。
continuation(即“await”之后的行或 catch 内的 block )稍后将在 T1 或其他线程上执行,具体取决于一系列因素。
Task.Delay。此代码调用 cancellationToken.Cancel。对 Cancel 的调用是在线程 T2 上进行的。我希望线程 T2 通过返回到 Cancel 的调用者来继续。我还希望看到 catch (OperationCanceledException) 的内容很快在线程 T1 或 T2 以外的某个线程上执行。
接下来发生的事情令人惊讶。我看到在线程 T2 上,调用 Cancel 后,执行会立即继续 catch (OperationCanceledException) 中的 block 。当 Cancel 仍在调用堆栈中时,就会发生这种情况。就好像对 Cancel 的调用被它被取消的代码劫持了一样。下面是显示此调用堆栈的 Visual Studio 屏幕截图:
更多上下文
这里有一些关于实际代码作用的更多上下文:
有一个累积请求的“ worker 代码”。一些“客户端代码”正在提交请求。每隔几秒钟,“工作代码”就会处理这些请求。已处理的请求将从队列中删除。
然而,偶尔,“客户端代码”决定它到达了它希望立即处理请求的地步。为了将此信息传达给“工作代码”,它调用了“工作代码”提供的方法 Jolt。由“客户端代码”调用的方法 Jolt 通过取消由 worker 代码主循环执行的 Task.Delay 来实现此功能。工作人员的代码已取消其 Task.Delay 并继续处理已排队的请求。
实际代码被简化为最简单的形式,代码为 available on GitHub .
环境
此问题可以在控制台应用程序、Windows 通用应用程序后台代理和 Windows Phone 8.1 通用应用程序后台代理程序中重现。
此问题无法在适用于 Windows 的通用应用程序中重现,其中代码按我预期的方式工作,并且对 Cancel 的调用会立即返回。
最佳答案
CancellationTokenSource.Cancel 不只是设置 IsCancellationRequested 标志。
CancallationToken 类有一个 Register method ,它允许您注册将在取消时调用的回调。这些回调由 CancellationTokenSource.Cancel 调用。
让我们来看看source code :
public void Cancel()
{
Cancel(false);
}
public void Cancel(bool throwOnFirstException)
{
ThrowIfDisposed();
NotifyCancellation(throwOnFirstException);
}
这是 NotifyCancellation方法:
private void NotifyCancellation(bool throwOnFirstException)
{
// fast-path test to check if Notify has been called previously
if (IsCancellationRequested)
return;
// If we're the first to signal cancellation, do the main extra work.
if (Interlocked.CompareExchange(ref m_state, NOTIFYING, NOT_CANCELED) == NOT_CANCELED)
{
// Dispose of the timer, if any
Timer timer = m_timer;
if(timer != null) timer.Dispose();
//record the threadID being used for running the callbacks.
ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId;
//If the kernel event is null at this point, it will be set during lazy construction.
if (m_kernelEvent != null)
m_kernelEvent.Set(); // update the MRE value.
// - late enlisters to the Canceled event will have their callbacks called immediately in the Register() methods.
// - Callbacks are not called inside a lock.
// - After transition, no more delegates will be added to the
// - list of handlers, and hence it can be consumed and cleared at leisure by ExecuteCallbackHandlers.
ExecuteCallbackHandlers(throwOnFirstException);
Contract.Assert(IsCancellationCompleted, "Expected cancellation to have finished");
}
}
好的,现在要注意的是 ExecuteCallbackHandlers 可以在目标上下文或当前上下文中执行回调。我让你看看ExecuteCallbackHandlers method source code因为在这里包含它有点太长了。但有趣的是:
if (m_executingCallback.TargetSyncContext != null)
{
m_executingCallback.TargetSyncContext.Send(CancellationCallbackCoreWork_OnSyncContext, args);
// CancellationCallbackCoreWork_OnSyncContext may have altered ThreadIDExecutingCallbacks, so reset it.
ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId;
}
else
{
CancellationCallbackCoreWork(args);
}
我猜你现在开始明白我接下来要看的地方了……当然是Task.Delay。让我们看看它的source code :
// Register our cancellation token, if necessary.
if (cancellationToken.CanBeCanceled)
{
promise.Registration = cancellationToken.InternalRegisterWithoutEC(state => ((DelayPromise)state).Complete(), promise);
}
嗯……那是什么InternalRegisterWithoutEC method ?
internal CancellationTokenRegistration InternalRegisterWithoutEC(Action<object> callback, Object state)
{
return Register(
callback,
state,
false, // useSyncContext=false
false // useExecutionContext=false
);
}
唉。 useSyncContext=false - 这解释了您看到的行为,因为 ExecuteCallbackHandlers 中使用的 TargetSyncContext 属性将为 false。由于未使用同步上下文,取消在 CancellationTokenSource.Cancel 的调用上下文中执行。
关于c# - 对 CancellationTokenSource.Cancel 的调用永远不会返回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31495411/
为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返
设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案
我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘
我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案
我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
所以我开始关注ruby,很多东西看起来不错,但我对隐式return语句很反感。我理解默认情况下让所有内容返回self或nil但不是语句的最后一个值。对我来说,它看起来非常脆弱(尤其是)如果你正在使用一个不打算返回某些东西的方法(尤其是一个改变状态/破坏性方法的函数!),其他人可能最终依赖于一个返回对方法的目的并不重要,并且有很大的改变机会。隐式返回有什么意义?有没有办法让事情变得更简单?总是有返回以防止隐含返回被认为是好的做法吗?我是不是太担心这个了?附言当人们想要从方法中返回特定的东西时,他们是否经常使用隐式返回,这不是让你组中的其他人更容易破坏彼此的代码吗?当然,记录一切并给出
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
当我执行>rvminstall1.9.2时一切顺利。然后我做>rvmuse1.9.2也很顺利。但是当涉及到ruby-v时..sam@sjones:~$rvminstall1.9.2/home/sam/.rvm/rubies/ruby-1.9.2-p136,thismaytakeawhiledependingonyourcpu(s)...ruby-1.9.2-p136-#fetchingruby-1.9.2-p136-#downloadingruby-1.9.2-p136,thismaytakeawhiledependingonyourconnection...%Total%Rece
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www