- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在尝试使用新的 SemaphoreSlim
.NET 4.0 中的类来限制可以无限期运行的快节奏循环的速率。在对此进行单元测试时,我发现如果循环足够紧密且并行度足够高,SemaphoreSlim
当你调用 Release()
时会抛出一个无法捕获的异常, 即使您检查了 .Count
属性优先,并在整个检查计数/释放序列期间锁定信号量实例本身。
此异常会导致应用程序停止运行。据我所知,没有捕获它。
深入挖掘,我发现 SemaphoreSlim
正在尝试访问它自己的 .AvailableWaitHandle
Release()
期间的内部属性(property)调用,它在那里抛出异常,而不是我访问 SemaphoreSlim
实例本身。 (我必须使用调试-> 异常-> 公共(public)语言运行时异常-> 在 Visual Studio 中抛出所有检查来发现这一点;您无法在运行时捕获它。有关更多详细信息,请参见 The Uncatchable Exception。)
我的问题是,在这种情况下,有没有人知道使用此类而不会冒应用立即终止风险的安全方法?
注意:信号量实例被包裹在一个RateGate实例中,其代码可以在这篇文章中找到:Better Rate Limiting in .NET .
更新:我正在添加完整的控制台应用程序代码来重现。两个答案都有助于解决问题;请参阅下面的解释。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Linq;
using System.Text;
using System.Threading;
using PennedObjects.RateLimiting;
namespace RateGateForceTerminateDemo
{
class Program
{
static int _secondsToRun = 10;
static void Main(string[] args) {
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
OptimizeMaxThreads();
Console.WriteLine();
Console.WriteLine("Press any key to exit.");
Console.ReadKey(true);
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) {
Console.WriteLine("Unhandled exception, terminating={0}:{1}", e.IsTerminating, e.ExceptionObject.ToString());
Console.WriteLine("Press any key to terminate app.");
Console.ReadKey(true);
}
static void OptimizeMaxThreads() {
int processors = Environment.ProcessorCount;
int processorsSq = Convert.ToInt32(Math.Pow(processors,2));
int threads = 1;
double result;
Tuple<int, double> maxResult = new Tuple<int, double>(threads, 0);
while (threads <= processorsSq) {
Console.WriteLine("Running for {0}s with upper limit of {1} threads... ", _secondsToRun, threads);
result = TestThrottling(10000000, threads, _secondsToRun);
Console.WriteLine("Ok. Result is {0:N0} ops/s", result);
Console.WriteLine();
if(result > maxResult.Item2)
maxResult = new Tuple<int, double>(threads, result);
threads *= 2;
}
Console.WriteLine("{0} threads achieved max throughput of {1:N0}", maxResult.Item1, maxResult.Item2);
}
static double TestThrottling(int limitPerSecond, int maxThreads, int maxRunTimeSeconds) {
int completed = 0;
RateGate gate = new RateGate(limitPerSecond, TimeSpan.FromSeconds(1));
ParallelLoopResult res = new ParallelLoopResult();
ParallelOptions parallelOpts = new ParallelOptions() { MaxDegreeOfParallelism = maxThreads };
Stopwatch sw = Stopwatch.StartNew();
try {
res = Parallel.For<int>(0, 1000000000, parallelOpts, () => 0, (num, state, subtotal) =>
{
bool succeeded = gate.WaitToProceed(10000);
if (succeeded) {
subtotal++;
}
else {
Console.WriteLine("Gate timed out for thread {0}; {1:N0} iterations, elapsed {2}.", Thread.CurrentThread.ManagedThreadId, subtotal, sw.Elapsed);
// return subtotal;
}
if (sw.Elapsed.TotalSeconds > maxRunTimeSeconds) {
Console.WriteLine("MaxRunTime expired for thread {0}, last succeeded={1}, iterations={2:N0}, elapsed={3}.", Thread.CurrentThread.ManagedThreadId, succeeded, subtotal, sw.Elapsed);
state.Break();
}
return subtotal;
}, (subtotal) => Interlocked.Add(ref completed, subtotal));
}
catch (AggregateException aggEx) {
Console.WriteLine(aggEx.Flatten().ToString());
}
catch (Exception ex) {
Console.WriteLine(ex);
}
sw.Stop();
double throughput = completed / Math.Max(sw.Elapsed.TotalSeconds, 1);
Console.WriteLine("Done at {0}, finished {1:N0} iterations, IsCompleted={2}, LowestBreakIteration={3:N0}, ",
sw.Elapsed,
completed,
res.IsCompleted,
(res.LowestBreakIteration.HasValue ? res.LowestBreakIteration.Value : double.NaN));
Console.WriteLine();
//// Uncomment the following 3 lines to stop prevent the ObjectDisposedException:
//Console.WriteLine("We should not hit the dispose statement below without a console pause.");
//Console.Write("Hit any key to continue... ");
//Console.ReadKey(false);
gate.Dispose();
return throughput;
}
}
}
因此使用@dtb 的解决方案,线程“a”仍然有可能通过 _isDisposed
检查,但线程“b”在线程“a”命中 Release()
之前处理信号量.我发现在 ExitTimerCallback 和 Dispose 方法中都在 _semaphore 实例周围添加了一个锁。 @Peter Ritchie 的建议让我在处理信号量之前取消并处理计时器。这两件事的结合让程序完成并正确处理 RateGate,无一异常(exception)。
因为如果没有那个输入我就不会得到这个我不想回答自己。但是,由于 StackOverflow 在提供完整答案时更有用,所以我会接受首先发布补丁或伪补丁并在上述控制台应用程序中成功存活的人。
最佳答案
问题出在您使用的 RateGate 类中。它有一个内部 Timer,即使在 RateGate 实例被释放后也会运行它的代码。此代码包括对已处置的 SemaphoreSlim 的 Release 调用。
修复:
@@ -88,7 +88,8 @@
int exitTime;
while (_exitTimes.TryPeek(out exitTime)
&& unchecked(exitTime - Environment.TickCount) <= 0)
{
+ if (_isDisposed) return;
_semaphore.Release();
_exitTimes.TryDequeue(out exitTime);
}
关于c# - 如何在不冒应用程序失败风险的情况下调用 SemaphoreSlim.Release()?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10526760/
我想知道是否SemaphoreSlim调用 Await 时具有优先级之类的东西。 我找不到任何东西,但也许以前有人做过这样的事情。 这个想法是,如果我需要,可以稍后以更高的优先级在信号量上调用等待,并
Documentation说: The SemaphoreSlim class represents a lightweight, fast semaphore that can be used fo
目前,我正在努力实现 SemaphoreSlim 以“锁定”必须是线程安全的方法的“部分”。我的问题是,在没有异常处理过载的情况下实现它是非常困难的。因为在释放“锁”之前抛出异常时,它将永远留在那里。
我创建了以下方法 TestThrottled 来尝试限制我的任务,但它根本没有限制,当我调用 WhenAll 并且此方法都具有相同的耗时时。我做错了什么吗? private static as
我们正在使用 .NET 4.5.1 开发一个应用程序,并在后台使用 SemaphoreSlim 实现了我自己的“异步锁”。要锁定,我们使用以下方法: public async Task LockAsy
semaphore 的真正力量是: Limits the number of threads that can access a resource or pool of resources concu
我在 .NET Core (2.2) 中使用 SemaphoreSlim 类时遇到问题,希望有人可以提供帮助。 我有一个 API 方法 (AddBooking),它将预订添加到数据库,此方法包括 3
概括 我有一个使用 lock 的类(class)提供对私有(private)字段的线程安全访问。但是,由于下面详述的原因,我正在考虑改用 SemaphoreSlim实现线程安全。我知道如果我用 loc
为了同步,我创建了一个 SemaphoreSlim(1)。 这意味着如果我在我的类中有这个信号量的单个实例作为锁: private SemaphoreSlim _initializationSemap
我正在尝试使用新的 SemaphoreSlim .NET 4.0 中的类来限制可以无限期运行的快节奏循环的速率。在对此进行单元测试时,我发现如果循环足够紧密且并行度足够高,SemaphoreSlim当
这个有效: int _counter; readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); async void Button_C
using System; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp2 { clas
我对 await 关键字的理解是,await 限定语句之后的代码在完成后作为该语句的延续运行。 因此以下两个版本应该产生相同的输出: public static Task Run(Semaph
class Program { static IEnumerable list = Enumerable.Range(1, 10).Select(i => new site(i.ToStrin
我在 Async/Await 函数中有一部分代码,我希望一次只执行一个线程。 这相对简单,只需创建一个新的 SemaphoreSlim(1) 并使用 WaitAsync/Release。效果是第一个线
根据文档: "a SemaphoreSlim doesn't use a Windows kernel semaphore". SemaphoreSlim 是否使用了任何特殊资源,这使得在不再使用 S
SemaphoreSlim 的文档说“只有在所有其他操作都已完成时才应使用 Dispose”。 应该如何调整以下类,以便线程 B 可以在线程 A 等待 Async() 时调用 Dispose()。调用
附加问题 SemaphoreSlim(1, 1) 是否仍然确保我有正确的输出 1000000,即使 task1 和 task2 在 2 个不同的平台上运行核心? 原始问题 考虑以下代码片段,_sema
我最近开始学习 C#,但在更新 UI 时遇到了运行多线程的问题。根据我目前所学,SemaphoreSlim 似乎是运行多线程同时仍控制最大并发线程数的正确方法。 场景:我想向网站(例如http://w
我遇到了一个问题,我需要限制对另一个网络服务器的调用次数。它会有所不同,因为服务器是共享的,并且可能具有更多或更少的容量。 我正在考虑使用 SemaphoreSlim 类,但没有公共(public)属
我是一名优秀的程序员,十分优秀!