草庐IT

C# AutoResetEvent 不释放

coder 2024-01-18 原文

使用 Xamarin.Forms(适用于 iOS),我尝试实现在继续之前等待用户确认已设置 GeoLocation 权限的功能。

我尝试实现这一点的方法是让线程等待直到使用 AutoResetEvent 触发事件。 .

主要问题(我相信)位于以下代码中:

manager.AuthorizationChanged += (object sender, CLAuthorizationChangedEventArgs args) => {

    Console.WriteLine ("Authorization changed to: {0}", args.Status);

    if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
        tcs.SetResult (args.Status == CLAuthorizationStatus.AuthorizedAlways || args.Status == CLAuthorizationStatus.AuthorizedWhenInUse);
    } else {
        tcs.SetResult (args.Status == CLAuthorizationStatus.Authorized);
    }

    _waitHandle.Set ();
};

manager.Failed += (object sender, Foundation.NSErrorEventArgs e) => {

    Console.WriteLine ("Authorization failed");

    tcs.SetResult (false);

    _waitHandle.Set ();
};

if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
    manager.RequestWhenInUseAuthorization ();
}

_waitHandle.WaitOne ();

您可以在下面找到完整的类(class):
public class LocationManager : ILocationManager
{
    static EventWaitHandle _waitHandle = new AutoResetEvent (false);

    private TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();

    public LocationManager ()
    {
    }

    public Task<bool> IsGeolocationEnabledAsync()
    {
        Console.WriteLine (String.Format("Avaible on device: {0}", CLLocationManager.LocationServicesEnabled));
        Console.WriteLine (String.Format("Permission on device: {0}", CLLocationManager.Status));

        if (!CLLocationManager.LocationServicesEnabled) {
            tcs.SetResult (false);
        } else if (CLLocationManager.Status == CLAuthorizationStatus.Denied || CLLocationManager.Status == CLAuthorizationStatus.Restricted) {
            tcs.SetResult (false);
        } else if (CLLocationManager.Status == CLAuthorizationStatus.NotDetermined) {

            Console.WriteLine ("Waiting for authorisation");

            CLLocationManager manager = new CLLocationManager ();

            manager.AuthorizationChanged += (object sender, CLAuthorizationChangedEventArgs args) => {

                Console.WriteLine ("Authorization changed to: {0}", args.Status);

                if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
                    tcs.SetResult (args.Status == CLAuthorizationStatus.AuthorizedAlways || args.Status == CLAuthorizationStatus.AuthorizedWhenInUse);
                } else {
                    tcs.SetResult (args.Status == CLAuthorizationStatus.Authorized);
                }

                _waitHandle.Set ();
            };

            manager.Failed += (object sender, Foundation.NSErrorEventArgs e) => {

                Console.WriteLine ("Authorization failed");

                tcs.SetResult (false);

                _waitHandle.Set ();
            };

            if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
                manager.RequestWhenInUseAuthorization ();
            }

            _waitHandle.WaitOne ();

            Console.WriteLine (String.Format ("Auth complete: {0}", tcs.Task.Result));

        } else {
            if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
                tcs.SetResult (CLLocationManager.Status == CLAuthorizationStatus.AuthorizedAlways || CLLocationManager.Status == CLAuthorizationStatus.AuthorizedWhenInUse);
            } else {
                tcs.SetResult (CLLocationManager.Status == CLAuthorizationStatus.Authorized);
            }
        }

        return tcs.Task;
    }
}

它工作正常,只是我不知道为什么 manager.AuthorizationChangedmanager.Failed事件似乎永远不会被触发,因此当状态未确定时线程永远不会释放。

非常感谢任何帮助或指示。

最佳答案

a good, minimal, complete code example可靠地重现问题,不可能确切地知道问题是什么。但是你的代码肯定有一个明显的设计缺陷,我希望解决这个缺陷会解决你的问题。

有什么缺陷?你正在等待任何事情。你写了一个显然应该代表异步操作的方法——它的名称中有“Async”并返回一个 Task<bool>而不是 bool - 然后你以这样的方式编写方法,Task<bool>无论采用哪个代码路径,返回的内容都将始终完成。

为什么这如此糟糕?好吧,除了它完全无法利用您正在实现的接口(interface)的异步方面这一简单事实之外,很可能 CLLocationManager您正在使用的类希望能够在您的 IsGeolocationEnabledAsync() 所在的同一线程中运行方法被调用。由于此方法在引发事件之前不会返回,并且由于在方法返回之前无法引发事件,因此您会遇到死锁。

恕我直言,这就是您的类(class)应该如何实现:

public class LocationManager : ILocationManager
{
    public async Task<bool> IsGeolocationEnabledAsync()
    {
        bool result;

        Console.WriteLine (String.Format("Avaible on device: {0}", CLLocationManager.LocationServicesEnabled));
        Console.WriteLine (String.Format("Permission on device: {0}", CLLocationManager.Status));

        if (!CLLocationManager.LocationServicesEnabled) {
            result = false;
        } else if (CLLocationManager.Status == CLAuthorizationStatus.Denied || CLLocationManager.Status == CLAuthorizationStatus.Restricted) {
            result = false;
        } else if (CLLocationManager.Status == CLAuthorizationStatus.NotDetermined) {
            TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();

            Console.WriteLine ("Waiting for authorisation");

            CLLocationManager manager = new CLLocationManager ();

            manager.AuthorizationChanged += (object sender, CLAuthorizationChangedEventArgs args) => {

                Console.WriteLine ("Authorization changed to: {0}", args.Status);

                if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
                    tcs.SetResult (args.Status == CLAuthorizationStatus.AuthorizedAlways || args.Status == CLAuthorizationStatus.AuthorizedWhenInUse);
                } else {
                    tcs.SetResult (args.Status == CLAuthorizationStatus.Authorized);
                }
            };

            manager.Failed += (object sender, Foundation.NSErrorEventArgs e) => {

                Console.WriteLine ("Authorization failed");

                tcs.SetResult (false);
            };

            if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
                manager.RequestWhenInUseAuthorization ();
                result = await tcs.Task;
            } else {
                result = false;
            }

            Console.WriteLine (String.Format ("Auth complete: {0}", tcs.Task.Result));

        } else {
            if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
                result = CLLocationManager.Status == CLAuthorizationStatus.AuthorizedAlways || CLLocationManager.Status == CLAuthorizationStatus.AuthorizedWhenInUse;
            } else {
                result = CLLocationManager.Status == CLAuthorizationStatus.Authorized;
            }
        }

        return result;
    }
}

IE。把方法变成 async方法,不要理会TaskCompletionSource除非你真的需要等待任何东西,然后await CLLocationManager 的结果的异步操作,返回其结果。

即使在您调用 CLLocationManager.RequestWhenInUseAuthorization() 的情况下,这也将允许该方法立即返回。 ,而不改变调用者的语义(即它仍然看到 Task<bool> 返回值并且可以 await 结果)。如果该方法同步完成,则调用者实际上不必等待。如果没有,那么假设调用者已经正确写入并且它本身没有阻塞等待结果的线程,操作将能够正常完成,设置完成源的结果并让等待它的代码继续进行.

笔记:
  • 以上假设在您的平台中您可以使用 async/await特征。如果你不这样做,调整以适应是很简单的;它与上面的技术基本相同,只是您实际上会使用 TaskCompletionSource对于方法中的所有分支,而不仅仅是异步分支,然后将返回 tcs.Task值(value)就像你以前一样。请注意,在这种情况下,您必须调用 ContinueWith()如果您想要最后一个 Console.WriteLine(),请在您的方法中明确说明以正确的顺序执行,即在操作实际完成之后。
  • 您的代码也有似乎是一个可能的错误。也就是你只拨打RequestWhenInUseAuthorization()有条件地,如果系统版本符合您的预期。这也可能导致您尝试修复的行为,假设 RequestWhenInUseAuthorization()方法是最终导致这些事件中的任何一个引发的原因。如果您不调用该方法,那么显然不会引发任何事件,并且您的代码将永远等待。我搬家了await进同if子句与方法调用本身,并简单地将结果设置为 falseelse条款。我没有足够的上下文来确定这一切应该如何工作;我假设您对正在使用的 API 足够熟悉,如果我的假设不正确,您可以解决任何剩余的细节问题。
  • 最后,我想强调的是,假设您的问题实际上是阻塞当前线程是阻止您尝试完成异步操作的原因,那么从此方法中删除等待是必要的,但还不够。你必须确保调用链中没有任何东西在等待返回的 Task<bool>以及或以其他方式阻塞线程。我在上面提到了这一点,但确保这一点非常重要,而且您没有提供完整的代码示例,因此我无法确保情况如此,因此我想确保这一非常重要的点不会被忽视。
  • 关于C# AutoResetEvent 不释放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33553640/

    有关C# AutoResetEvent 不释放的更多相关文章

    1. ruby - 如何强制 Ruby 释放内存给操作系统 - 2

      正如标题,我有一个处理大量数据的ruby​​程序。该程序占用了所有内存,其中调用了系统命令hostname,并且发生错误无法分配内存-主机名我试过GC.start但它不起作用。那么如何强制ruby释放未使用的内存呢?OK,这是别人的测试代码,最后报错是big_var被回收了。但是内存仍然没有释放。require"weakref"defreportputs"#{param}:\t\tMemory"+`psax-opid,rss|grep-E"^[[:space:]]*#{$$}"`.strip.split.map(&:to_i)[1].to_s+'KB'endbig_var=""#big

    2. c - 在不使用其他功能的情况下释放 C 扩展中的全局 VM 锁 - 2

      我不明白为什么在发布或获取RubyCAPI中的GVL时需要另一个间接级别。rb_thread_call_without_gvl()和rb_thread_call_with_gvl()都需要一个只接受一个参数的函数,但情况并非总是如此。我不想仅仅为了发布GVL而将我的参数包装在一个结构中。它使代码的可读性变得复杂,并且需要从void指针转换到void指针。在查看Ruby的线程代码后,我找到了GVL_UNLOCK_BEGIN。/GVL_UNLOCK_END与Python的Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS匹配的宏但我找不到关于它们以及何时

    3. ruby - Sidekiq 在工作人员完成后不释放内存 - 2

      我有大约6个Sidekiqworker执行JSON爬行。根据端点的数据集大小,它们在1分钟到4小时之间完成。特别是,观看需要4小时的长视频,我发现随着时间的推移,内存有非常轻微的增加。这不是问题,直到我想再次安排相同的worker作业。内存不会被释放并堆积起来,直到我遇到LinuxOOMKiller,它摆脱了我的Sidekiq进程。内存泄漏?我观察了ObjectSpace中不同对象的数量:ObjectSpace.each_object.inject(Hash.new(0)){|count,o|count[o.class]+=1}那里并没有真正增加,哈希集、数组等保持不变,垃圾收集器清除

    4. javascript - 释放 Javascript 中未附加的 DOM 节点使用的内存 - 2

      作为我的应用程序的一部分,我将一组不会同时显示的小型Dom节点放在一起。我将它们存储在一个内部数组中。用户可以调用他们的显示,在这种情况下,我将它们重新设置为用于显示它们的div。这一切都很好。但是当需要用新的替换它们时,我想销毁旧的(有效地释放它们)。否则,随着时间的推移,内存使用量可能呈指数级增长。如何强制浏览器js引擎执行此操作?只是将我的Dom节点数组中的每个项目都设置为null就足够了吗?还有什么我必须做的吗?或者也许我根本不必担心这个? 最佳答案 如果您将每个项目设置为null,它们将被自动垃圾回收。

    5. javascript - 释放 Javascript 对象使用的内存 - 2

      我应该自己释放分配的内存,还是有一种垃圾收集器?可以在JavaScript中使用以下代码吗?functionfillArray(){varc=newArray;c.push(3);c.push(2);returnc;}vararr=fillArray();vard=arr.pop()谢谢 最佳答案 引自AppleJavaScriptCodingGuidelines:Usedeletestatements.Wheneveryoucreateanobjectusinganewstatement,pairitwithadeletestat

    6. javascript - Three.js - 释放内存 - 2

      我使用内置的形状挤出功能沿着样条线挤出形状。每次移动样条曲线的节点时,我都会创建一个新网格。但是这个我的内存很快就满了。每次我创建一个新的网格时,我都会删除旧的scene.__removeObject(mesh);但它不会释放已用内存。我测试了FirefoxNightly和Chrome,如果内存已满,它们都会崩溃。我搜索了一般的WebGL功能和Three.js相关的解决方案,但没有找到任何东西。也许具有更多WebGL/Three.js知识的人可以给我提示。谢谢 最佳答案 确保您没有在其他任何地方引用javascript网格对象,以便

    7. javascript - Windows 7 小工具不释放 ActiveX 对象 - 2

      我正在开发一个需要从Excel文档中提取数据的Windows7小工具。问题是,在我检索到我需要的数据后,Excel进程不会卸载。这是我在初始化函数中使用的代码:varExcel=newActiveXObject("Excel.Application");Excel.Visible=false;Excel.DisplayAlerts=false;varworkbooks=Excel.Workbooks;varworkbook=workbooks.Open("\\\\SERVER\\Documents\\Sample.xlsx",0,true);varactivesheet=workboo

    8. javascript - 在javascript中释放数组数组的最佳方法 - 2

      在javascript中释放数组的数组以确保不会发生内存泄漏的最佳方法是什么?varfoo=newArray();foo[0]=newArray();foo[0][0]='bar0';foo[0][1]='bar1';foo[1]=newArray();...删除(foo)?遍历foo、delete(foo[index])和delete(foo)?1和2给我相同的结果?没有? 最佳答案 foo=null;应该足以让垃圾收集器摆脱数组,包括它的所有子数组(假设没有其他东西引用它们)。请注意,它只会在需要时摆脱它,而不是立即摆脱它,所以

    9. javascript - XMLHttpRequest - 使用后释放? - 2

      我正在编写一个完全由AJAX驱动的浏览器应用程序(我人生中的第一次),这意味着:会在浏览器中停留一个页面,根据需要加载程序组件浏览器历史记录将是,好吧,没有。页面根本不会刷新我关心的是我应该如何处理XMLHttpRequests,因为我主要是C++程序员,当你写一个像这样的语句时被教导x=newXMLHttpRequest();之后您需要删除它。这个问题完全是关于内存管理的,这个用new分配的对象是否保留在内存中,即使它完成了它的“循环”readyState==4或者以某种方式释放,释放,whatchacallit?老实说,我不知道什么时候可以释放它,因为创建这些的脚本将在HEAD中并

    10. Javascript 自动释放资源(如 RAII) - 2

      我的一般问题是我可以使用什么技术来确保在Javascript中清理/释放资源?目前,我正在采用C(不使用goto)方法在我的函数中查找返回或异常的每条执行路径,并确保进行清理。我的具体示例是这样的:在Node.js中,我在对象成员函数中使用互斥锁(通过文件锁)(我需要互斥,因为我运行Node.js应用程序的多个实例并且在不同时有竞争条件实例与文件系统交互)。例如,在C++中,我会执行如下操作:voidMyClass::dangerous(void){MyLocklock(&this->mutex);...//attheendofthisfunction,lockwillbedestru

    随机推荐