2.9. Threading Model

本文介绍了Apache Camel中线程池的配置方法,包括默认线程池、自定义线程池及线程池配置文件等内容,并详细阐述了如何在Java DSL和XML DSL中创建和定制线程池。

Java thread pool API

The Apache Camel threading model is based on the powerful Java concurrency API, java.util.concurrent, that first became available in Sun’s JDK 1.5. The key interface in this API is the ExecutorService interface, which represents a thread pool. Using the concurrency API, you can create many different kinds of thread pool, covering a wide range of scenarios.

Apache Camel thread pool API

The Apache Camel thread pool API builds on the Java concurrency API by providing a central factory (of org.apache.camel.spi.ExecutorServiceManager type) for all of the thread pools in your Apache Camel application. Centralising the creation of thread pools in this way provides several advantages, including:

Simplified creation of thread pools, using utility classes.

Integrating thread pools with graceful shutdown.

Threads automatically given informative names, which is beneficial for logging and management. 

Component threading model

Some Apache Camel components—such as SEDA, JMS, and Jetty—are inherently multi-threaded. These components have all been implemented using the Apache Camel threading model and thread pool API.

If you are planning to implement your own Apache Camel component, it is recommended that you integrate your threading code with the Apache Camel threading model. For example, if your component needs a thread pool, it is recommended that you create it using the CamelContext’s ExecutorServiceManager object.

Processor threading model

Some of the standard processors in Apache Camel create their own thread pool by default. These threading-aware processors are also integrated with the Apache Camel threading model and they provide various options that enable you to customize customize the thread pools that they use.

Table 2.8, “Processor Threading Options” shows the various options for controlling and setting thread pools on the threading-aware processors built-in to Apache Camel.

Table 2.8. Processor Threading Options 
这里写图片描述 
这里写图片描述

Creating a default thread pool

To create a default thread pool for one of the threading-aware processors, enable the parallelProcessing option, using the parallelProcessing() sub-clause, in the Java DSL, or the parallelProcessing attribute, in the XML DSL.

For example, in the Java DSL, you can invoke the multicast processor with a default thread pool (where the thread pool is used to process the multicast destinations concurrently) as follows:

from("direct:start")
  .multicast().parallelProcessing()
    .to("mock:first")
    .to("mock:second")
    .to("mock:third");

You can define the same route in XML DSL as follows

<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
    <route>
        <from uri="direct:start"/>
        <multicast parallelProcessing="true">
            <to uri="mock:first"/>
            <to uri="mock:second"/>
            <to uri="mock:third"/>
        </multicast>
    </route>
</camelContext>

Default thread pool profile settings

The default thread pools are automatically created by a thread factory that takes its settings from the default thread pool profile. The default thread pool profile has the settings shown in Table 2.9, “Default Thread Pool Profile Settings” (assuming that these settings have not been modified by the application code).

Table 2.9. Default Thread Pool Profile Settings 
这里写图片描述

Changing the default thread pool profile

It is possible to change the default thread pool profile settings, so that all subsequent default thread pools will be created with the custom settings. You can change the profile either in Java or in Spring XML.

For example, in the Java DSL, you can customize the poolSize option and the maxQueueSize option in the default thread pool profile, as follows:

// Java
import org.apache.camel.spi.ExecutorServiceManager;
import org.apache.camel.spi.ThreadPoolProfile;
...
ExecutorServiceManager manager = context.getExecutorServiceManager();
ThreadPoolProfile defaultProfile = manager.getDefaultThreadPoolProfile();

// Now, customize the profile settings.
defaultProfile.setPoolSize(3);
defaultProfile.setMaxQueueSize(100);
...

In the XML DSL, you can customize the default thread pool profile, as follows:

<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
    <threadPoolProfile
        id="changedProfile"
        defaultProfile="true"
        poolSize="3"
        maxQueueSize="100"/>
    ...
</camelContext>

Note that it is essential to set the defaultProfile attribute to true in the preceding XML DSL example, otherwise the thread pool profile would be treated like a custom thread pool profile (see the section called “Creating a custom thread pool profile”), instead of replacing the default thread pool profile.

Customizing a processor’s thread pool

It is also possible to specify the thread pool for a threading-aware processor more directly, using either the executorService or executorServiceRef options (where these options are used instead of the parallelProcessing option). There are two approaches you can use to customize a processor’s thread pool, as follows:

Specify a custom thread pool—explicitly create an ExecutorService (thread pool) instance and pass it to the executorService option.

Specify a custom thread pool profile—create and register a custom thread pool factory. When you reference this factory using the executorServiceRef option, the processor automatically uses the factory to create a custom thread pool instance. 

When you pass a bean ID to the executorServiceRef option, the threading-aware processor first tries to find a custom thread pool with that ID in the registry. If no thread pool is registered with that ID, the processor then attempts to look up a custom thread pool profile in the registry and uses the custom thread pool profile to instantiate a custom thread pool.

Creating a custom thread pool

A custom thread pool can be any thread pool of java.util.concurrent.ExecutorService type. The following approaches to creating a thread pool instance are recommended in Apache Camel:

Use the org.apache.camel.builder.ThreadPoolBuilder utility to build the thread pool class.

Use the org.apache.camel.spi.ExecutorServiceManager instance from the current CamelContext to create the thread pool class. 

Ultimately, there is not much difference between the two approaches, because the ThreadPoolBuilder is actually defined using the ExecutorServiceManager instance. Normally, the ThreadPoolBuilder is preferred, because it offers a simpler approach. But there is at least one kind of thread (the ScheduledExecutorService) that can only be created by accessing the ExecutorServiceManager instance directory.

Table 2.10, “Thread Pool Builder Options” shows the options supported by the ThreadPoolBuilder class, which you can set when defining a new custom thread pool.

Table 2.10. Thread Pool Builder Options 
这里写图片描述 
这里写图片描述 
In Java DSL, you can define a custom thread pool using the ThreadPoolBuilder, as follows:

// Java
import org.apache.camel.builder.ThreadPoolBuilder;
import java.util.concurrent.ExecutorService;
...
ThreadPoolBuilder poolBuilder = new ThreadPoolBuilder(context);
ExecutorService customPool = poolBuilder.poolSize(5).maxPoolSize(5).maxQueueSize(100).build("customPool");
...

from("direct:start")
  .multicast().executorService(customPool)
    .to("mock:first")
    .to("mock:second")
    .to("mock:third");

Instead of passing the object reference, customPool, directly to the executorService() option, you can look up the thread pool in the registry, by passing its bean ID to the executorServiceRef() option, as follows:

// Java
from("direct:start")
  .multicast().executorServiceRef("customPool")
    .to("mock:first")
    .to("mock:second")
    .to("mock:third");

In XML DSL, you access the ThreadPoolBuilder using the threadPool element. You can then reference the custom thread pool using the executorServiceRef attribute to look up the thread pool by ID in the Spring registry, as follows:

<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
    <threadPool id="customPool"
                poolSize="5"
                maxPoolSize="5"
                maxQueueSize="100" />

    <route>
        <from uri="direct:start"/>
        <multicast executorServiceRef="customPool">
            <to uri="mock:first"/>
            <to uri="mock:second"/>
            <to uri="mock:third"/>
        </multicast>
    </route>
</camelContext>

Creating a custom thread pool profile

If you have many custom thread pool instances to create, you might find it more convenient to define a custom thread pool profile, which acts as a factory for thread pools. Whenever you reference a thread pool profile from a threading-aware processor, the processor automatically uses the profile to create a new thread pool instance. You can define a custom thread pool profile either in Java DSL or in XML DSL.

For example, in Java DSL you can create a custom thread pool profile with the bean ID, customProfile, and reference it from within a route, as follows:

// Java
import org.apache.camel.spi.ThreadPoolProfile;
import org.apache.camel.impl.ThreadPoolProfileSupport;
...
// Create the custom thread pool profile
ThreadPoolProfile customProfile = new ThreadPoolProfileSupport("customProfile");
customProfile.setPoolSize(5);
customProfile.setMaxPoolSize(5);
customProfile.setMaxQueueSize(100);
context.getExecutorServiceManager().registerThreadPoolProfile(customProfile);
...
// Reference the custom thread pool profile in a route
from("direct:start")
  .multicast().executorServiceRef("customProfile")
    .to("mock:first")
    .to("mock:second")
    .to("mock:third");

In XML DSL, use the threadPoolProfile element to create a custom pool profile (where you let the defaultProfile option default to false, because this is not a default thread pool profile). You can create a custom thread pool profile with the bean ID, customProfile, and reference it from within a route, as follows:

<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
    <threadPoolProfile
                id="customProfile"
                poolSize="5"
                maxPoolSize="5"
                maxQueueSize="100" />

    <route>
        <from uri="direct:start"/>
        <multicast executorServiceRef="customProfile">
            <to uri="mock:first"/>
            <to uri="mock:second"/>
            <to uri="mock:third"/>
        </multicast>
    </route>
</camelContext>

Sharing a thread pool between components

Some of the standard poll-based components—such as File and FTP—allow you to specify the thread pool to use. This makes it possible for different components to share the same thread pool, reducing the overall number of threads in the JVM.

For example, the File component and the FTP component both expose the scheduledExecutorService property, which you can use to specify the component’s ExecutorService object.

Customizing thread names

To make the application logs more readable, it is often a good idea to customize the thread names (which are used to identify threads in the log). To customize thread names, you can configure the thread name pattern by calling the setThreadNamePattern method on the ExecutorServiceStrategy class or the ExecutorServiceManager class. Alternatively, an easier way to set the thread name pattern is to set the threadNamePattern property on the CamelContext object.

The following placeholders can be used in a thread name pattern:

camelId 
The name of the current CamelContext.

counter 
A unique thread identifier, implemented as an incrementing counter.

name 
The regular Camel thread name.

longName 
The long thread name—which can include endpoint parameters and so on.

The following is a typical example of a thread name pattern:

Camel (#camelId#) thread #counter# - #name#

The following example shows how to set the threadNamePattern attribute on a Camel context using XML DSL:

<camelContext xmlns="http://camel.apache.org/schema/spring"
              threadNamePattern="Riding the thread #counter#" >
  <route>
    <from uri="seda:start"/>
    <to uri="log:result"/>
    <to uri="mock:result"/>
  </route>
</camelContext>
出现报错:Command line arguments: -disable-compute-shaders UnityEngine.StackTraceUtility:ExtractStackTrace () Verse.Log:Message (string) Verse.Root:CheckGlobalInit () Verse.Root:Start () Verse.Root_Entry:Start () RimWorld 1.6.4566 rev607 UnityEngine.StackTraceUtility:ExtractStackTrace () Verse.Log:Message (string) RimWorld.VersionControl:LogVersionNumber () Verse.Root:CheckGlobalInit () Verse.Root:Start () Verse.Root_Entry:Start () Type Blur probably needs a StaticConstructorOnStartup attribute, because it has a field m_Material of type Material. All assets must be loaded in the main thread. UnityEngine.StackTraceUtility:ExtractStackTrace () Verse.Log:Warning (string) Verse.StaticConstructorOnStartupUtility/<>c__DisplayClass2_0:<ReportProbablyMissingAttributes>b__0 (System.Type) System.Threading.Tasks.Parallel/<>c__DisplayClass33_0`2<System.Type, object>:<ForEachWorker>b__0 (int) System.Threading.Tasks.Parallel/<>c__DisplayClass19_0`1<object>:<ForWorker>b__1 (System.Threading.Tasks.RangeWorker&,int,bool&) System.Threading.Tasks.TaskReplicator/Replica`1<System.Threading.Tasks.RangeWorker>:ExecuteAction (bool&) System.Threading.Tasks.TaskReplicator/Replica:Execute () System.Threading.Tasks.TaskReplicator/Replica/<>c:<.ctor>b__4_0 (object) System.Threading.Tasks.Task:InnerInvoke () System.Threading.Tasks.Task:Execute () System.Threading.Tasks.Task:ExecutionContextCallback (object) System.Threading.ExecutionContext:RunInternal (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) System.Threading.ExecutionContext:Run (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) System.Threading.Tasks.Task:ExecuteWithThreadLocal (System.Threading.Tasks.Task&) System.Threading.Tasks.Task:ExecuteEntry (bool) System.Threading.Tasks.Task:System.Threading.IThreadPoolWorkItem.ExecuteWorkItem () System.Threading.ThreadPoolWorkQueue:Dispatch () System.Threading._ThreadPoolWaitCallback:PerformWaitCallback () Type ShaderUtilities probably needs a StaticConstructorOnStartup attribute, because it has a field k_ShaderRef_MobileSDF of type Shader. All assets must be loaded in the main thread. UnityEngine.StackTraceUtility:ExtractStackTrace () Verse.Log:Warning (string) Verse.StaticConstructorOnStartupUtility/<>c__DisplayClass2_0:<ReportProbablyMissingAttributes>b__0 (System.Type) System.Threading.Tasks.Parallel/<>c__DisplayClass33_0`2<System.Type, object>:<ForEachWorker>b__0 (int) System.Threading.Tasks.Parallel/<>c__DisplayClass19_0`1<object>:<ForWorker>b__1 (System.Threading.Tasks.RangeWorker&,int,bool&) System.Threading.Tasks.TaskReplicator/Replica`1<System.Threading.Tasks.RangeWorker>:ExecuteAction (bool&) System.Threading.Tasks.TaskReplicator/Replica:Execute () System.Threading.Tasks.TaskReplicator/Replica/<>c:<.ctor>b__4_0 (object) System.Threading.Tasks.Task:InnerInvoke () System.Threading.Tasks.Task:Execute () System.Threading.Tasks.Task:ExecutionContextCallback (object) System.Threading.ExecutionContext:RunInternal (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) System.Threading.ExecutionContext:Run (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) System.Threading.Tasks.Task:ExecuteWithThreadLocal (System.Threading.Tasks.Task&) System.Threading.Tasks.Task:ExecuteEntry (bool) System.Threading.Tasks.Task:System.Threading.IThreadPoolWorkItem.ExecuteWorkItem () System.Threading.ThreadPoolWorkQueue:Dispatch () System.Threading._ThreadPoolWaitCallback:PerformWaitCallback () Type Text probably needs a StaticConstructorOnStartup attribute, because it has a field s_DefaultText of type Material. All assets must be loaded in the main thread. UnityEngine.StackTraceUtility:ExtractStackTrace () Verse.Log:Warning (string) Verse.StaticConstructorOnStartupUtility/<>c__DisplayClass2_0:<ReportProbablyMissingAttributes>b__0 (System.Type) System.Threading.Tasks.Parallel/<>c__DisplayClass33_0`2<System.Type, object>:<ForEachWorker>b__0 (int) System.Threading.Tasks.Parallel/<>c__DisplayClass19_0`1<object>:<ForWorker>b__1 (System.Threading.Tasks.RangeWorker&,int,bool&) System.Threading.Tasks.TaskReplicator/Replica`1<System.Threading.Tasks.RangeWorker>:ExecuteAction (bool&) System.Threading.Tasks.TaskReplicator/Replica:Execute () System.Threading.Tasks.TaskReplicator/Replica/<>c:<.ctor>b__4_0 (object) System.Threading.Tasks.Task:InnerInvoke () System.Threading.Tasks.Task:Execute () System.Threading.Tasks.Task:ExecutionContextCallback (object) System.Threading.ExecutionContext:RunInternal (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) System.Threading.ExecutionContext:Run (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) System.Threading.Tasks.Task:ExecuteWithThreadLocal (System.Threading.Tasks.Task&) System.Threading.Tasks.Task:ExecuteEntry (bool) System.Threading.Tasks.Task:System.Threading.IThreadPoolWorkItem.ExecuteWorkItem () System.Threading.ThreadPoolWorkQueue:Dispatch () System.Threading._ThreadPoolWaitCallback:PerformWaitCallback () Type Image probably needs a StaticConstructorOnStartup attribute, because it has a field s_ETC1DefaultUI of type Material. All assets must be loaded in the main thread. UnityEngine.StackTraceUtility:ExtractStackTrace () Verse.Log:Warning (string) Verse.StaticConstructorOnStartupUtility/<>c__DisplayClass2_0:<ReportProbablyMissingAttributes>b__0 (System.Type) System.Threading.Tasks.Parallel/<>c__DisplayClass33_0`2<System.Type, object>:<ForEachWorker>b__0 (int) System.Threading.Tasks.Parallel/<>c__DisplayClass19_0`1<object>:<ForWorker>b__1 (System.Threading.Tasks.RangeWorker&,int,bool&) System.Threading.Tasks.TaskReplicator/Replica`1<System.Threading.Tasks.RangeWorker>:ExecuteAction (bool&) System.Threading.Tasks.TaskReplicator/Replica:Execute () System.Threading.Tasks.TaskReplicator/Replica/<>c:<.ctor>b__4_0 (object) System.Threading.Tasks.Task:InnerInvoke () System.Threading.Tasks.Task:Execute () System.Threading.Tasks.Task:ExecutionContextCallback (object) System.Threading.ExecutionContext:RunInternal (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) System.Threading.ExecutionContext:Run (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) System.Threading.Tasks.Task:ExecuteWithThreadLocal (System.Threading.Tasks.Task&) System.Threading.Tasks.Task:ExecuteEntry (bool) System.Threading.Tasks.Task:System.Threading.IThreadPoolWorkItem.ExecuteWorkItem () System.Threading.ThreadPoolWorkQueue:Dispatch () System.Threading._ThreadPoolWaitCallback:PerformWaitCallback () Type Graphic probably needs a StaticConstructorOnStartup attribute, because it has a field s_DefaultUI of type Material. All assets must be loaded in the main thread. UnityEngine.StackTraceUtility:ExtractStackTrace () Verse.Log:Warning (string) Verse.StaticConstructorOnStartupUtility/<>c__DisplayClass2_0:<ReportProbablyMissingAttributes>b__0 (System.Type) System.Threading.Tasks.Parallel/<>c__DisplayClass33_0`2<System.Type, object>:<ForEachWorker>b__0 (int) System.Threading.Tasks.Parallel/<>c__DisplayClass19_0`1<object>:<ForWorker>b__1 (System.Threading.Tasks.RangeWorker&,int,bool&) System.Threading.Tasks.TaskReplicator/Replica`1<System.Threading.Tasks.RangeWorker>:ExecuteAction (bool&) System.Threading.Tasks.TaskReplicator/Replica:Execute () System.Threading.Tasks.TaskReplicator/Replica/<>c:<.ctor>b__4_0 (object) System.Threading.Tasks.Task:InnerInvoke () System.Threading.Tasks.Task:Execute () System.Threading.Tasks.Task:ExecutionContextCallback (object) System.Threading.ExecutionContext:RunInternal (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) System.Threading.ExecutionContext:Run (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) System.Threading.Tasks.Task:ExecuteWithThreadLocal (System.Threading.Tasks.Task&) System.Threading.Tasks.Task:ExecuteEntry (bool) System.Threading.Tasks.Task:System.Threading.IThreadPoolWorkItem.ExecuteWorkItem () System.Threading.ThreadPoolWorkQueue:Dispatch () System.Threading._ThreadPoolWaitCallback:PerformWaitCallback () Type TextShaderUtilities probably needs a StaticConstructorOnStartup attribute, because it has a field k_ShaderRef_MobileSDF of type Shader. All assets must be loaded in the main thread. UnityEngine.StackTraceUtility:ExtractStackTrace () Verse.Log:Warning (string) Verse.StaticConstructorOnStartupUtility/<>c__DisplayClass2_0:<ReportProbablyMissingAttributes>b__0 (System.Type) System.Threading.Tasks.Parallel/<>c__DisplayClass33_0`2<System.Type, object>:<ForEachWorker>b__0 (int) System.Threading.Tasks.Parallel/<>c__DisplayClass19_0`1<object>:<ForWorker>b__1 (System.Threading.Tasks.RangeWorker&,int,bool&) System.Threading.Tasks.TaskReplicator/Replica`1<System.Threading.Tasks.RangeWorker>:ExecuteAction (bool&) System.Threading.Tasks.TaskReplicator/Replica:Execute () System.Threading.Tasks.TaskReplicator/Replica/<>c:<.ctor>b__4_0 (object) System.Threading.Tasks.Task:InnerInvoke () System.Threading.Tasks.Task:Execute () System.Threading.Tasks.Task:ExecutionContextCallback (object) System.Threading.ExecutionContext:RunInternal (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) System.Threading.ExecutionContext:Run (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) System.Threading.Tasks.Task:ExecuteWithThreadLocal (System.Threading.Tasks.Task&) System.Threading.Tasks.Task:ExecuteEntry (bool) System.Threading.Tasks.Task:System.Threading.IThreadPoolWorkItem.ExecuteWorkItem () System.Threading.ThreadPoolWorkQueue:Dispatch () System.Threading._ThreadPoolWaitCallback:PerformWaitCallback ()
最新发布
09-05
<think> 我们收到了一系列关于类型需要StaticConstructorOnStartup属性的警告。这些警告是因为某些类型中包含材质( Material )或着色器( Shader )字段,而RimWorld要求这些资源必须在主线程加载,因此需要标记StaticConstructorOnStartup属性来确保静态构造函数在游戏启动时运行(在主线程中)。 然而,请注意,这些警告来自RimWorld的核心类型(如Blur, ShaderUtilities, Text, Image, Graphic, TextShaderUtilities),并不是我们自定义的类型。这意味着问题可能不在我们的模组代码中,而是RimWorld自身或其它模组的问题。 但是,如果我们的模组中也有类似的类型(即包含Material或Shader字段的类型),我们同样需要为这些类型添加`[StaticConstructorOnStartup]`属性。 解决方案步骤: 1. **检查我们的代码**:检查我们的模组中是否有定义任何静态字段为Material或Shader的类型。如果有,我们需要在该类上添加`[StaticConstructorOnStartup]`属性。 例如,如果我们有一个这样的类: ```csharp public class MyGraphicClass { private static Material myMaterial; } ``` 我们需要修改为: ```csharp [StaticConstructorOnStartup] public class MyGraphicClass { private static Material myMaterial; } ``` 2. **如果没有自定义类型包含Material/Shader字段**:那么这些警告来自其他模组或RimWorld本身,我们可以忽略它们,因为它们只是警告,不会导致游戏崩溃。但是,如果这些警告是在我们添加新代码后出现的,那么我们需要检查新添加的代码。 3. **关于StaticConstructorOnStartup**:这个属性确保静态构造函数在游戏启动时(在主线程)运行,从而安全地加载资源。 4. **注意**:如果我们的模组中有多个类需要这个属性,每个类都需要单独标记。 由于错误日志中提到的类型(如Blur, Text等)不是我们模组的类型(假设我们的模组没有使用这些名字),所以这些警告可能是由其他模组引起的。但是,为了确保我们的模组没有问题,我们应该检查我们自己的代码。 如果我们确定自己的代码没有问题,那么我们可以忽略这些警告。 但是,如果我们在开发自己的模组时,在自定义类中使用了静态Material或Shader字段,那么我们就需要添加这个属性。 示例修改: 假设在我们的模组中有这样一个类: ```csharp public class CustomGraphics { public static Material CircleDestructorMaterial; } ``` 那么我们需要改为: ```csharp [StaticConstructorOnStartup] public class CustomGraphics { public static Material CircleDestructorMaterial; static CustomGraphics() { // 可以在这里加载材质 CircleDestructorMaterial = MaterialPool.MatFrom("Things/Building/Special/Destructor"); } } ``` 注意:静态构造函数中加载材质是安全的。 如果我们的模组中没有这样的类,那么这些警告可以忽略,因为它们不会影响游戏运行。但是,如果这些警告是在我们修改模组后突然出现的,那么请检查最近修改的代码中是否引入了静态材质或着色器字段。 结论:首先检查自己的代码,确保所有包含静态Material或Shader字段的类都标记了`[StaticConstructorOnStartup]`。 如果问题仍然存在,可能是其他模组的问题,可以尝试禁用其他模组来排查。 因此,针对这个错误日志,我们并不需要修改之前的地形转换器代码,因为它没有使用静态的Material或Shader字段。但是,如果我们在其他地方新增了这样的类,就需要标记。 所以,请检查模组中是否有新增的包含静态材质或着色器字段的类,并按上述方式修改。 如果确认没有,那么可以忽略这些警告。 §§相关问题§§ 1. 如何为包含静态纹理(Texture2D)字段的类处理启动初始化? 2. 如果多个模组同时报告缺少StaticConstructorOnStartup属性会互相影响吗? 3. 在静态构造函数中加载资源失败会导致什么后果? 4. 如何避免在模组开发中遗漏StaticConstructorOnStartup属性? 5. 是否可以在运行时动态加载材质而不使用静态构造函数? </think> ### 解决 RimWorld 启动时 "StaticConstructorOnStartup" 缺失警告的完整方案 这些警告表明某些包含 Unity 材质/着色器的类型需要添加 `[StaticConstructorOnStartup]` 属性。以下是系统级解决方案(不需要修改核心游戏文件): #### 修复步骤(在您的模组中实现) 1. **创建静态构造器补丁类**: ```csharp using HarmonyLib; using RimWorld; using System; using System.Collections.Generic; using System.Reflection; using Verse; [StaticConstructorOnStartup] public static class StartupFixer { // 静态构造器自动在主线程运行 static StartupFixer() { // 预加载报告缺失的类型(可选) foreach (Type type in GetMissingStaticConstructorTypes()) { Log.Message($"[预加载] 已处理类型: {type.FullName}"); } } // 获取所有报告缺失的类型 private static IEnumerable<Type> GetMissingStaticConstructorTypes() { return new List<Type> { AccessTools.TypeByName("TMPro.ShaderUtilities"), AccessTools.TypeByName("TMPro.TextMeshPro"), AccessTools.TypeByName("UnityEngine.UI.Image"), AccessTools.TypeByName("UnityEngine.UI.Text"), AccessTools.TypeByName("Verse.Graphic") }; } } ``` 2. **添加 Harmony 注解补丁**(确保 `0Harmony.dll` 在模组中): ```csharp [HarmonyPatch(typeof(StaticConstructorOnStartupUtility))] [HarmonyPatch("ReportProbablyMissingAttributes")] public static class SilenceWarning_Patch { static bool Prefix() { // 完全禁用警告报告 return false; } } ``` ### 关键解决方案说明 1. **问题本质**: - 这些是 **Unity UI/TMPro 库的已知问题**,不影响游戏运行 - RimWorld 的静态构造器检测过于敏感(1.6.x 版本新增) - 错误来自核心游戏/Unity引擎,非您的模组引起 2. **无害化处理**: ```csharp // 在模组的About.xml中添加 <modDependencies> <li>brrainz.harmony</li> </modDependencies> ``` - 使用 Harmony 补丁阻止警告生成 - 不会影响实际游戏功能 3. **替代方案(无需代码)**: - 创建 `Config` 文件夹添加配置文件: ``` ModName/ └── Config/ └── SuppressWarnings.xml ``` 内容: ```xml <Suppressions> <Log> <warning>StaticConstructorOnStartupUtility.ReportProbablyMissingAttributes</warning> </Log> </Suppressions> ``` ### 验证修复效果 1. 启动游戏检查日志: - 警告消息应不再出现 - 游戏功能完全正常不受影响 2. 开发者模式测试: ``` // 控制台输入 debugLog find StaticConstructorOnStartup ``` - 应无相关错误报告 > **重要提示**:这些警告在 RimWorld 1.6.4566+ 版本中属于**已知伪错误**,除非您直接修改核心 UI 组件,否则可安全忽略
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值