c_cpp 用于记录主线程上过多阻塞的类

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c_cpp 用于记录主线程上过多阻塞的类相关的知识,希望对你有一定的参考价值。

#import "GHRunLoopWatchdog.h"
#include <mach/mach_time.h>

// The default number of seconds that must pass to consider a run loop stalled.
static const NSTimeInterval GHRunLoopWatchdogDefaultStallingThreshold = 0.2;

@interface GHRunLoopWatchdog ()

// The run loop to watch.
//
// Despite being marked `assign`, this property is retained.
@property (nonatomic, assign, readonly) CFRunLoopRef runLoop;

// The observer used to watch the run loop.
//
// Despite being marked `assign`, this property is retained.
@property (nonatomic, assign, readonly) CFRunLoopObserverRef observer;

// The number of seconds that must pass to consider the run loop stalled.
@property (nonatomic, assign, readonly) NSTimeInterval threshold;

// The mach_absolute_time() at which the current run loop iteration was started,
// or 0 if there is no current iteration in progress.
//
// This property is not thread-safe, and must only be accessed from the thread
// that the run loop is associated with.
@property (nonatomic, assign) uint64_t startTime;

// Invoked any time the run loop stalls.
//
// duration - The number of seconds that elapsed in the run loop iteration.
- (void)iterationStalledWithDuration:(NSTimeInterval)duration;

@end

@implementation GHRunLoopWatchdog

#pragma mark Lifecycle

- (id)initWithRunLoop:(CFRunLoopRef)runLoop {
	return [self initWithRunLoop:runLoop stallingThreshold:GHRunLoopWatchdogDefaultStallingThreshold];
}

- (id)initWithRunLoop:(CFRunLoopRef)runLoop stallingThreshold:(NSTimeInterval)threshold {
	NSParameterAssert(runLoop != NULL);
	NSParameterAssert(threshold > 0);

	self = [super init];
	if (self == nil) return nil;

	_runLoop = (CFRunLoopRef)CFRetain(runLoop);
	_threshold = threshold;

	// Precalculate timebase information.
	mach_timebase_info_data_t timebase;
	mach_timebase_info(&timebase);

	NSTimeInterval secondsPerMachTime = timebase.numer / timebase.denom / 1e9;

	@weakify(self);

	// Observe at an extremely low order so that we can catch stalling even in
	// high-priority operations (like UI redrawing or animation).
	_observer = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopAllActivities, YES, INT_MIN, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
		@strongify(self);

		switch (activity) {
			// What we consider one "iteration" might start with any one of
			// these events.
			case kCFRunLoopEntry:
			case kCFRunLoopBeforeTimers:
			case kCFRunLoopAfterWaiting:
			case kCFRunLoopBeforeSources:
				if (self.startTime == 0) self.startTime = mach_absolute_time();
				break;

			case kCFRunLoopBeforeWaiting:
			case kCFRunLoopExit: {
				uint64_t endTime = mach_absolute_time();
				if (self.startTime <= 0) {
					break;
				}

				uint64_t elapsed = endTime - self.startTime;

				NSTimeInterval duration = elapsed * secondsPerMachTime;
				if (duration > self.threshold) [self iterationStalledWithDuration:duration];
				
				self.startTime = 0;
				break;
			}

			default:
				NSAssert(NO, @"Observer should not have been triggered for activity %i", (int)activity);
		}
	});

	if (_observer == NULL) return nil;

	return self;
}

- (void)dealloc {
	if (_observer != NULL) {
		CFRunLoopObserverInvalidate(_observer);

		CFRelease(_observer);
		_observer = NULL;
	}

	if (_runLoop != NULL) {
		CFRelease(_runLoop);
		_runLoop = NULL;
	}
}

#pragma mark Starting and Stopping

- (void)startWatchingMode:(CFStringRef)mode {
	NSParameterAssert(mode != NULL);
	CFRunLoopAddObserver(self.runLoop, self.observer, mode);
}

- (void)stopWatchingMode:(CFStringRef)mode {
	NSParameterAssert(mode != NULL);
	CFRunLoopRemoveObserver(self.runLoop, self.observer, mode);
}

#pragma mark Timing

- (void)iterationStalledWithDuration:(NSTimeInterval)duration {
	#if DEBUG
	NSLog(@"%@: iteration of run loop %p took %.f ms to execute", self, self.runLoop, (double)duration * 1000);
	#endif

	void (^didStall)(NSTimeInterval) = self.didStallWithDuration;
	if (didStall != nil) didStall(duration);
}

@end
/// Observes a run loop to detect any stalling or blocking that occurs.
///
/// This class is thread-safe.
@interface GHRunLoopWatchdog : NSObject

/// Initializes the receiver to watch the specified run loop, using a default
/// stalling threshold.
- (id)initWithRunLoop:(CFRunLoopRef)runLoop;

/// Initializes the receiver to detect when the specified run loop blocks for
/// more than `threshold` seconds.
///
/// This is the designated initializer for this class.
- (id)initWithRunLoop:(CFRunLoopRef)runLoop stallingThreshold:(NSTimeInterval)threshold;

/// Begins watching the receiver's run loop for stalling in the given mode.
///
/// The receiver will automatically stop watching the run loop upon deallocation.
///
/// mode - The mode in which to monitor the specified run loop. Use
///        kCFRunLoopCommonModes to watch all common run loop modes. This should
///        not be NULL.
- (void)startWatchingMode:(CFStringRef)mode;

/// Stops watching the receiver's run loop for stalling in the given mode.
///
/// There is generally no need to invoke this method explicitly.
///
/// mode - The mode in which to monitor the specified run loop. Use
///        kCFRunLoopCommonModes to watch all common run loop modes. This should
///        not be NULL.
- (void)stopWatchingMode:(CFStringRef)mode;

/// A block to invoke any time the run loop stalls.
///
/// duration - The number of seconds that elapsed in the run loop iteration.
@property (copy) void (^didStallWithDuration)(NSTimeInterval duration);

@end

以上是关于c_cpp 用于记录主线程上过多阻塞的类的主要内容,如果未能解决你的问题,请参考以下文章

.NET - 阻塞主线程,直到有任何可用线程

当主线程显然未阻塞时,QProgressDialog 冻结

如何解除阻塞已删除命名管道上的线程阻塞?

NSThead的进阶使用和简单探讨

当 Cocoa 应用程序中的主线程被阻塞时,UI 不会更新

Android主线程阻塞WebView线程