对于想了解累积布局偏移(CLS)问题在wordpress主题中的读者,本文将是一篇不可错过的文章,并且为您提供关于C#好代码学习笔记(1):文件操作、读取文件、Debug/Trace类、Conditi
对于想了解累积布局偏移 (CLS) 问题在 wordpress 主题中的读者,本文将是一篇不可错过的文章,并且为您提供关于C# 好代码学习笔记(1):文件操作、读取文件、Debug/Trace 类、Conditional条件编译、CLS、cls 挂钩或错误使用中的内存泄漏? CLS 挂钩异步钩子、java#Class
- 累积布局偏移 (CLS) 问题在 wordpress 主题中
- C# 好代码学习笔记(1):文件操作、读取文件、Debug/Trace 类、Conditional条件编译、CLS
- cls 挂钩或错误使用中的内存泄漏? CLS 挂钩异步钩子
- java#Class
cls && T t - Manager isn''t accessible via %s instances" % cls.__name_ 报错信息
累积布局偏移 (CLS) 问题在 wordpress 主题中
如何解决累积布局偏移 (CLS) 问题在 wordpress 主题中
我的 wordpress/woocommerce 商店中有付费主题,而在 pagespeed 洞察中,我遇到了无法解决的 CLS 问题。我尝试询问主题的支持团队,但他们没有帮助我。 pagespeed 告诉我问题出在这个元素上:
<div class="large-6 small-12 columns product-info summary entry-summary rtl-left">
示例页面为: page link
我不想直接编辑主题。是否可以添加adicional css或片段? 谢谢你的帮助。
C# 好代码学习笔记(1):文件操作、读取文件、Debug/Trace 类、Conditional条件编译、CLS
目录- 1,文件操作
- 2,读取文件
- 3,Debug 、Trace类
- 4,条件编译
- 5,MethodImpl 特性
- 5,CLSCompliantAttribute
- 6,必要时自定义类型别名
目录:
1,文件操作
2,Debug、Trace类
3,条件编译
4,MethodImpl 特性
5,CLSComplianAttribute
6,必要时自定义类型别名
最近在阅读 .NET Core Runtime 的源码,参考大佬的代码,学习编写技巧和提高代码水平。学习过程中将学习心得和值得应用到项目中的代码片段记录下来,供日后查阅。
1,文件操作
这段代码在 System.Private.CoreLib
下,对 System.IO.File 中的代码进行精简,供 CLR 使用。
当使用文件时,要提前判断文件路径是否存在,日常项目中要使用到文件的地方应该不少,可以统一一个判断文件是否存在的方法:
public static bool Exists(string? path) { try { // 可以将 string? 改成 string if (path == null) return false; if (path.Length == 0) return false; path = Path.GetFullPath(path); // After normalizing, check whether path ends in directory separator. // Otherwise, FillAttributeInfo removes it and we may return a false positive. // GetFullPath should never return null Debug.Assert(path != null, "File.Exists: GetFullPath returned null"); if (path.Length > 0 && PathInternal.IsDirectorySeparator(path[^1])) { return false; } return InternalExists(path); } catch (ArgumentException) { } catch (NotSupportedException) { } // Security can throw this on ":" catch (SecurityException) { } catch (IOException) { } catch (UnauthorizedAccessException) { } return false; }
建议项目中对路径进行最终处理的时候,都转换为绝对路径:
Path.GetFullPath(path)
当然,相对路径会被 .NET 正确识别,但是对于运维排查问题和各方面考虑,绝对路径容易定位具体位置和排错。
在编写代码时,使用相对路径,不要写死,提高灵活性;在运行阶段将其转为绝对路径;
上面的 NotSupportedException
等异常是操作文件中可能出现的各种异常情况,对于跨平台应用来说,这些异常可能都是很常见的,提前将其异常类型识别处理,可以优化文件处理逻辑以及便于筛查处理错误。
2,读取文件
这段代码在 System.Private.CoreLib 中。
有个读取文件转换为 byte[] 的方法如下:
public static byte[] ReadAllBytes(string path) { // bufferSize == 1 used to avoid unnecessary buffer in FileStream using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1)) { long fileLength = fs.Length; if (fileLength > int.MaxValue) throw new IOException(SR.IO_FileTooLong2GB); int index = 0; int count = (int)fileLength; byte[] bytes = new byte[count]; while (count > 0) { int n = fs.Read(bytes, index, count); if (n == 0) throw Error.GetEndOfFile(); index += n; count -= n; } return bytes; } }
可以看到 FileStream 的使用,如果单纯是读取文件内容,可以参考里面的代码:
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1)
上面的代码同样也存在 File.ReadAllBytes
与之对应, File.ReadAllBytes 内部是使用 InternalReadAllBytes
来处理文档读取:
private static byte[] InternalReadAllBytes(String path, bool checkHost) { byte[] bytes; // 此 FileStream 的构造函数不是 public ,开发者不能使用 using(FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, FileStream.DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false, false, checkHost)) { // Do a blocking read int index = 0; long fileLength = fs.Length; if (fileLength > Int32.MaxValue) throw new IOException(Environment.GetResourceString("IO.IO_FileTooLong2GB")); int count = (int) fileLength; bytes = new byte[count]; while(count > 0) { int n = fs.Read(bytes, index, count); if (n == 0) __Error.EndOfFile(); index += n; count -= n; } } return bytes; }
这段说明我们可以放心使用 File
静态类中的函数,因为里面已经处理好一些逻辑了,并且自动释放文件。
如果我们手动 new FileStream
,则要判断一些情况,以免使用时报错,最好参考一下上面的代码。
.NET 文件流缓存大小默认是 4096
字节:
internal const int DefaultBufferSize = 4096;
这段代码在 File 类中定义,开发者不能设置缓存块的大小,大多数情况下,4k 是最优的块大小。
ReadAllBytes 的文件大小上限是 2 GB。
3,Debug 、Trace类
这两个类的命名空间为 System.Diagnostics
,Debug 、Trace 提供一组有助于调试代码的方法和属性。
Debug 中的所有函数都不会在 Release 中有效,并且所有输出流不会在控制台显示,必须注册侦听器才能读取这些流。
Debug 可以打印调试信息并使用断言检查逻辑,使代码更可靠,而不会影响发运产品的性能和代码大小。
这类输出方法有 Write 、WriteLine 、 WriteIf 和 WriteLineIf 等,这里输出不会直接打印到控制台。
如需将调试信息打印到控制台,可以注册侦听器:
ConsoleTraceListener console = new ConsoleTraceListener();Trace.Listeners.Add(console);
注意, .NET Core 2.x 以上 Debug 没有 Listeners ,因为 Debug 使用的是 Trace 的侦听器。
我们可以给 Trace.Listeners 注册侦听器,这样相对于 Debug
等效设置侦听器。
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); Debug.WriteLine("aa");
.NET Core 中的监听器都继承了 TraceListener,如 TextWriterTraceListener、ConsoleTraceListener、DefaultTraceListener。
如果需要输出到文件中,可以自行继承 TextWriterTraceListener
,编写文件流输出,也可以使用 DelimitedListTraceListener。
示例:
TraceListener listener = new DelimitedListTraceListener(@"C:\debugfile.txt"); // Add listener. Debug.Listeners.Add(listener); // Write and flush. Debug.WriteLine("Welcome");
处理上述方法输出控制台,也可以使用
ConsoleTraceListener console=......Listeners.Add(console);// 等效于var console = new TextWriterTraceListener(Console.Out)
为了格式化输出流,可以使用 一下属性控制排版:
属性 | 说明 |
---|---|
AutoFlush | 获取或设置一个值,通过该值指示每次写入后是否应在 Flush() 上调用 Listeners。 |
IndentLevel | 获取或设置缩进级别。 |
IndentSize | 获取或设置缩进的空格数。 |
// 1. Debug.WriteLine("One"); // Indent and then unindent after writing. Debug.Indent(); Debug.WriteLine("Two"); Debug.WriteLine("Three"); Debug.Unindent(); // End. Debug.WriteLine("Four"); // Sleep. System.Threading.Thread.Sleep(10000);
One Two ThreeFour
.Assert()
方法对我们调试程序很有帮助,Assert 向开发人员发送一个强消息。在 IDE 中,断言会中断程序的正常操作,但不会终止应用程序。
.Assert()
的最直观效果是输出程序的断言位置。
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); int value = -1; // A. // If va.........
cls 挂钩或错误使用中的内存泄漏? CLS 挂钩异步钩子
正如向 OP 指出的那样,用法肯定是不正确的。
OP 应该只执行一次 ns.run()
,并且 run
中的所有内容都将具有相同的上下文。
看看这个正确用法的例子:
var createNamespace = require('cls-hooked').createNamespace;
var writer = createNamespace('writer');
writer.run(function () {
writer.set('value',0);
requestHandler();
});
function requestHandler() {
writer.run(function(outer) {
// writer.get('value') returns 0
// outer.value is 0
writer.set('value',1);
// writer.get('value') returns 1
// outer.value is 1
process.nextTick(function() {
// writer.get('value') returns 1
// outer.value is 1
writer.run(function(inner) {
// writer.get('value') returns 1
// outer.value is 1
// inner.value is 1
writer.set('value',2);
// writer.get('value') returns 2
// outer.value is 1
// inner.value is 2
});
});
});
setTimeout(function() {
// runs with the default context,because nested contexts have ended
console.log(writer.get('value')); // prints 0
},1000);
}
此外,cls-hooked
中的实现确实表明上下文是通过异步钩子回调 destroy(asyncId)
destroy(asyncID)
在 asyncId
对应的资源销毁后调用。它也被嵌入器 API 异步调用。某些 resources
依赖垃圾收集进行清理,因此如果对传递给 init
的资源对象进行了引用,则可能永远不会调用 destroy
,从而导致内存泄漏应用。如果资源不依赖于垃圾收集,那么这将不是问题。
https://github.com/Jeff-Lewis/cls-hooked/blob/0ff594bf6b2edd6fb046b10b67363c3213e4726c/context.js#L416-L425
这是我的存储库,通过使用 autocannon
用大量请求轰炸服务器来比较和测试内存使用情况
https://github.com/Darkripper214/AsyncMemoryTest
根据测试,heap
的利用率几乎没有增加(正如预期的那样,因为我们正在处理 HTTP 请求)。
CLS-Hooked 和 Async-Hook 的内存使用
目的
存储库是一个微型测试,用于查看使用 cls-hooked
和 async-hook
在 Node.js
内传递上下文时如何利用内存。
用法
-
npm run start
用于 CLS 挂钩服务器或npm run async
用于异步挂钩服务器 -
转到 Chrome 并粘贴
chrome://inspect
-
点击
inspect
访问服务器的开发工具
-
转到
memory
选项卡,您可以在用请求轰炸服务器之前、之中和之后拍摄快照并检查heap
-
node benchmark.js
开始用请求轰炸服务器。这是由autocannon
提供支持,您可能需要增加connections
或duration
以查看差异。
结果
CLS 挂钩
统计 | 1% | 2.5% | 50% | 97.5% | 平均 | 标准 | 最大 |
---|---|---|---|---|---|---|---|
请求/秒 | 839 | 839 | 871 | 897 | 870.74 | 14.23 | 839 |
字节/秒 | 237kB | 237kB | 246kB | 253kB | 246kB | 4.01kB | 237kB |
每秒采样一次的请求/字节计数(请注意,这是在附加调试器的情况下运行的,每秒性能会受到影响)
15.05 秒内 13k 个请求,3.68 MB 读取
异步钩子
统计 | 1% | 2.5% | 50% | 97.5% | 平均 | 标准 | 最大 |
---|---|---|---|---|---|---|---|
请求/秒 | 300 | 300 | 347 | 400 | 346.4 | 31.35 | 300 |
字节/秒 | 84.6kB | 84.6kB | 97.9kB | 113kB | 97.7kB | 8.84kB | 84.6kB |
每秒采样一次的请求/字节计数(请注意,这是在附加调试器的情况下运行的,并且有大量 debug()
消息显示它是如何被破坏的,每秒性能会受到影响)
15.15 秒内 5k 个请求,读取 1.47 MB
编辑 1
OP 抱怨每次执行 store
时设置的 _context
的长度。如前所述,OP 的测试方式不正确,因为它在循环中运行。
OP 所抱怨的场景只会在 namespace.run()
执行一些包含或包含 namespace.run()
的回调时发生。
async function
那么为什么_context 没有被清除?这是因为 async function t3() {} // This async function will cause _context length to not be cleared
function t2() {
t3();
}
function t1() {
for (let i = 0; i < 500; i++) {
session.run(t2);
}
}
t1();
将无法在 node.js async function t3
中运行,因为 event loop
持续运行,因此项目几乎无限附加到 {{1} }.
为了证明这是由于这种行为,我更新了 repo 以包含一个文件 synchronous for loop
,该文件可以使用 _context
运行,该文件在两者之间显式运行垃圾收集,以及垃圾集合不会影响 cls-gc.js
的长度。
npm run gc
和 _context
的执行过程中 _context
的长度会很长,因为两者都是同步的。但是,在调用 t1()
回调之后,t2()
的长度大约是正确的。请使用调试器进行检查。
_context
的长度将在 setTimeout
_context
java#Class cls && T t
/**
* 在阅读别人写的源代码中,有时候可以看到同样功能的泛型方法中的参数可以写成 T t 或者 Class<T> cls
* 那么,两者有什么区别呢?
*
* Class类,是代表加载到jvm中的类的字节码,这个类本身是泛型类,其后可以加<具体类>从而表示具体类的字节码类型,
* 于是Class<T>,就代表了类T的字节码类型。于是Class<T> cls中的cls就是类型T的字节码对象实例。
*
* 另一方面:
* T t 就很好理解了,T是方法返回值前面的泛型列表中指定的泛型类型,小t表示这个类型的对象。
*
* 请参考如下的三个方法仔细体会。
*
*/
/**
* 接受任意类型的对象,并返回该对象。
* @param t
* @param <T>
* @return
*/
public <T> T doSomething0(T t) {
return t;
}
/**
* 接受任意字节码实例,并通过该字节码创建实例并返回。
* 但是由于方法指定返回类型是T,所以返回时候要做强制类型转换,
* 因此传入的cls应该是返回值t对应类的字节码对象。
* 由于是强转没有做类型检查,所有使用了@SupperessWarning("unchecked")压制编译器产生的警告。
* @param cls
* @param <T>
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
public <T> T doSomething1(Class cls) throws Exception {
return (T) cls.newInstance();
}
/**
* 接受T类型的字节码对象,并根据此字节码对象创建对应类的实例并返回
* @param cls
* @param <T>
* @return
* @throws Exception
*/
public <T> T doSomething2(Class<T> cls) throws Exception {
return cls.newInstance();
}
/**
* 最后,
* Class对象实例可以用任意类型对象的.getClass()方法取得,
* Class对象的实例也可用任意类型.class 直接表示。
*
* Class<String> stringClass = String.class;
* Class<? extends String> a.getClass();
*/
Manager isn''t accessible via %s instances" % cls.__name_ 报错信息
Django 的 orm 中使用到了元类和描述符这些高级知识,了解一下的可以看看这篇文章。
元类其实就是用来定义类的,我的理解是这样的:当很多类有相同的属性,那么就可以提取这些相同的属性到一个类中,元类就是用来封装那些的,或者给某些类添加一些属性,定制类。你看看 Django model
写的代码有多少,而背后元类默默做了很多东西。
Django model
需要继承自 models.Model
,跟踪进去发现 class Model(metaclass=ModelBase):
,Model
其实是根据 ModelBase
构建的,在实例化的时候,会首先执行 ModelBase
的 __new__
方法。里面为我们封装了 _meta
这么一个属性,具体的可以看看源码。
我们查询数据的时候一直用到 obejcts
方法,那么它是哪来的呢?在封装self._meta
后调用了 _prepare
,在它里面发现了一个管理器 manager
。
if not opts.managers:
if any(f.name == ''objects'' for f in opts.fields):
raise ValueError(
"Model %s must specify a custom Manager, because it has a "
"field named ''objects''." % cls.__name__
)
manager = Manager()
manager.auto_created = True
cls.add_to_class(''objects'', manager)
这里使用了 manager
,然后注册了 objects
。那么来看看 Manager
:
class Manager(BaseManager.from_queryset(QuerySet)):
pass
什么也没做,只是根据 BaseManager
的类方法构造了一个类,然后继承了它,就是 QuerySet
,到这里,你就大体明白了吧,我们一般查询出来的都是一个 QuerySet
对象。
@classmethod
def from_queryset(cls, queryset_class, class_name=None):
if class_name is None:
class_name = ''%sFrom%s'' % (cls.__name__, queryset_class.__name__)
class_dict = {
''_queryset_class'': queryset_class,
}
class_dict.update(cls._get_queryset_methods(queryset_class))
return type(class_name, (cls,), class_dict)
这里使用了 type
来创造一个类。
其实准确来说,Manager
应该是继承了两个类 BaseManager
和 QuertSet
。
再来看看 add_to_class
:
def add_to_class(cls, name, value):
# We should call the contribute_to_class method only if it''s bound
if not inspect.isclass(value) and hasattr(value, ''contribute_to_class''):
value.contribute_to_class(cls, name)
else:
setattr(cls, name, value)
这里又调用了 manager
的 contribute_to_class
方法:
def contribute_to_class(self, model, name):
if not self.name:
self.name = name
self.model = model
setattr(model, name, ManagerDescriptor(self))
model._meta.add_manager(self)
又给 objects
赋值了一个 ManagerDescriptor
实例,这个是干嘛的呢?属性描述符:
class ManagerDescriptor:
def __init__(self, manager):
self.manager = manager
def __get__(self, instance, cls=None):
if instance is not None:
raise AttributeError("Manager isn''t accessible via %s instances" % cls.__name__)
if cls._meta.abstract:
raise AttributeError("Manager isn''t available; %s is abstract" % (
cls._meta.object_name,
))
if cls._meta.swapped:
raise AttributeError(
"Manager isn''t available; ''%s.%s'' has been swapped for ''%s''" % (
cls._meta.app_label,
cls._meta.object_name,
cls._meta.swapped,
)
)
return cls._meta.managers_map[self.manager.name]
这里做了一些限制。
Django orm
四个重要类:
Model
,QuerySet
,Query
,Objects
。
QuerySet
主要是定义了一些接口,如 filter
, count
等。它是惰性求值的,它并不直接求出值,只是在需要的时候查询。
Query
实现 sql
的拼接,它将语句交给 sql
编译对象。
一次查询过程:
如果还想深入,可以看看
QuerySet
的源码。
ps:
objects
是一个
Manager
实例,而
Manager
是一个空壳,它继承自
QuerySet
。
关于累积布局偏移 (CLS) 问题在 wordpress 主题中的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于C# 好代码学习笔记(1):文件操作、读取文件、Debug/Trace 类、Conditional条件编译、CLS、cls 挂钩或错误使用中的内存泄漏? CLS 挂钩异步钩子、java#Class
本文标签: