문제
문제(더 명확하게 하기 위해 단순화됨):
- 1.증가하는 함수가 있는 정적으로 연결된 static.lib가 하나 있습니다.
extern int CallCount = 0;
int TheFunction()
{
void *p = &CallCount;
printf("Function called");
return CallCount++;
}
2.static.lib는 TheFunction 메서드를 래핑하는 관리형 C++/CLI Managed.dll에 연결됩니다.
int Managed::CallLibFunc()
{
return TheFunction();
}
삼.테스트 앱에는 Managed.dll에 대한 참조가 있으며 C++/CLI 래퍼를 호출하는 여러 도메인을 만듭니다.
static void Main(string[] args)
{
Managed c1 = new Managed();
int val1 = c1.CallLibFunc();
// value is zero
AppDomain ad = AppDomain.CreateDomain("NewDomain");
Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed;
int val2 = c.CallLibFunc();
// value is one
}
질문:
Don Box의 Essential .NET Vol1 The CLR에서 읽은 내용에 따르면 CreateInstanceAndUnwrap이 호출될 때 Managed.dll/static.lib의 새로운 복사본이 로드되므로 val2가 0이 될 것으로 예상합니다.무슨 일이 일어나고 있는지 오해하고 있습니까?정적 라이브러리는 관리되지 않는 코드이기 때문에 appdomain 경계를 존중하지 않는 것 같습니다.관리형 인스턴스화를 위한 새로운 프로세스를 만드는 것 외에 이 문제를 해결할 수 있는 방법이 있습니까?
정말 감사합니다!
다른 팁
전화하신 후
Managed c1 = new Managed();
Managed.dll 래퍼가 애플리케이션의 기본 앱 도메인에 로드됩니다.static.lib의 관리되지 않는 도메인 항목이 있을 때까지 다른 도메인과 공유됩니다.별도의 프로세스를 만드는 대신 각 호출 전에 Managed.dll이 응용 프로그램 도메인에 로드되지 않았는지 확인하면 됩니다.
그거랑 비교해보세요
static void Main(string[] args)
{
{
AppDomain ad = AppDomain.CreateDomain("NewDomain");
Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed;
int val2 = c.CallLibFunc();
// Value is zero
AppDomain.Unload(ad)
}
{
AppDomain ad = AppDomain.CreateDomain("NewDomain");
Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed;
int val2 = c.CallLibFunc();
// I think value is zero
AppDomain.Unload(ad)
}
}
`
중요사항:한 줄만 추가하면 JIT 컴파일러가 Managed.dll을 로드하고 마법이 사라집니다.
static void Main(string[] args)
{
{
AppDomain ad = AppDomain.CreateDomain("NewDomain");
Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed;
int val2 = c.CallLibFunc();
// Value is zero
AppDomain.Unload(ad)
}
{
AppDomain ad = AppDomain.CreateDomain("NewDomain");
Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed;
int val2 = c.CallLibFunc();
// I think value is one
AppDomain.Unload(ad)
}
Managed c1 = new Managed();
}
이러한 라인에 의존하고 싶지 않다면 Managed.dll을 참조하고 호출 직후 도메인 언로드를 사용하여 별도의 도메인에서 각 호출을 수행하는 또 다른 래퍼 ManagedIsolated.dll을 만들 수 있습니다.기본 애플리케이션은 ManagedIsolated.dll 유형에만 의존하며 Managed.dll은 기본 앱 도메인에 로드되지 않습니다.
그것은 트릭처럼 보이지만 누군가에게는 유용할 수도 있습니다.`
요컨대 어쩌면.AppDomain은 순전히 관리되는 개념입니다.AppDomain이 인스턴스화되면 기본 DLL의 새 복사본에 매핑되지 않고 이미 메모리에 있는 코드를 재사용할 수 있습니다. 예를 들어 모든 System.* 어셈블리의 새 복사본을 로드할 것이라고는 예상할 수 없습니다. ?)
관리되는 세계 내에서 모든 정적 변수는 AppDomain에 의해 범위가 지정되지만, 지적한 대로 이는 관리되지 않는 세계에는 적용되지 않습니다.
각 앱 도메인에 대해 고유한 Managed.dll을 강제로 로드하는 복잡한 작업을 수행할 수 있으며, 이로 인해 새 버전의 정적 라이브러리가 함께 제공됩니다.예를 들어, 바이트 배열과 함께 Assembly.Load를 사용하면 작동할 수 있지만 동일한 어셈블리가 두 번 로드되는 경우 CLR이 유형의 충돌을 처리하려고 시도하는 방법을 모르겠습니다.
여기서는 실제 문제를 다루고 있지 않은 것 같습니다. 이 DDJ 기사를 참조하세요.
로더 최적화 속성의 기본값은 "AppDomain이 필요한 각 어셈블리 코드의 개인 복사본을 로드하게 하는" SingleDomain입니다.다중 도메인 값 중 하나이더라도 "모든 AppDomain은 항상 정적 필드의 고유한 복사본을 유지합니다".
'managed.dll'은 이름에서 알 수 있듯이 관리되는 어셈블리입니다.static.lib의 코드는 (IL 코드로) 'managed.dll'로 정적으로 컴파일되었으므로 Lenik이 기대하는 것과 동일한 동작을 기대할 수 있습니다....
...static.lib가 관리되지 않는 DLL에 대한 정적 내보내기 라이브러리가 아닌 경우.Lenik은 이것이 사실이 아니라고 말하므로 여기서 무슨 일이 일어나고 있는지 여전히 확신할 수 없습니다.
별도의 프로세스에서 실행해 보셨나요?정적 라이브러리는 자체 프로세스 외부에서 메모리 인스턴스를 공유해서는 안 됩니다.
관리하기가 어려울 수 있다는 것을 알고 있습니다.하지만 이 경우 다른 옵션이 무엇인지 잘 모르겠습니다.
편집하다:조금 둘러본 후에는 당신이 필요한 모든 것을 이 도구로 할 수 있을 것이라고 생각합니다. 시스템.진단.프로세스 수업.이 시점에서는 의사소통을 위한 많은 옵션이 있지만 .NET 원격 또는 WCF가 아마도 좋고 쉬운 선택일 것입니다.
이 주제에 관해 내가 찾은 최고의 기사 두 개는 다음과 같습니다.
- http://blogs.msdn.com/cbrumme/archive/2003/04/15/51317.aspx
- http://blogs.msdn.com/cbrumme/archive/2003/06/01/51466.aspx
중요한 부분은 다음과 같습니다.
RVA 기반 정적 필드는 프로세스 전역입니다.개체가 AppDomain 경계를 넘어 블리딩되는 것을 허용하지 않기 때문에 이는 스칼라 및 값 유형으로 제한됩니다.특히 AppDomain 언로드 중에 모든 종류의 문제가 발생할 수 있습니다.ILASM 및 MC++와 같은 일부 언어를 사용하면 RVA 기반 정적 필드를 정의하는 것이 편리합니다.대부분의 언어는 그렇지 않습니다.
좋습니다. .lib의 코드를 제어할 수 있다면 시도해 보겠습니다.
class CallCountHolder {
public:
CallCountHolder(int i) : count(i) {}
int count;
};
static CallCountHolder cc(0);
int TheFunction()
{
printf("Function called");
return cc.count++;
}
그는 RVA 기반 정적 필드가 스칼라 및 값 유형으로 제한된다고 말했기 때문입니다.int 배열도 작동할 수 있습니다.