|
| 1 | +--- |
| 2 | +audio: true |
| 3 | +lang: fr |
| 4 | +layout: post |
| 5 | +title: Objets Partagés dans Plusieurs Threads |
| 6 | +translated: true |
| 7 | +--- |
| 8 | + |
| 9 | +## Leçon |
| 10 | + |
| 11 | +Le code démontre un bug particulier qui apparaît de manière incohérente. Parfois, le bug se produit, et parfois il ne se produit pas, ce qui le rend difficile à reproduire et à déboguer. |
| 12 | + |
| 13 | +Ce comportement intermittent découle de la manière dont la fonction `translate_markdown_file`, en particulier la fonction `translate_front_matter`, gère les données partagées. Ces fonctions pourraient accéder et modifier des structures de données partagées, comme des dictionnaires ou des listes, sans une synchronisation appropriée. |
| 14 | + |
| 15 | +Lorsque plusieurs threads accèdent et modifient les mêmes données de manière concurrente, cela peut entraîner des conditions de course. Les conditions de course se produisent lorsque l'état final des données dépend de l'ordre imprévisible dans lequel les threads s'exécutent. Cela peut entraîner une corruption des données, un comportement inattendu du programme et les bugs intermittents que vous observez. |
| 16 | + |
| 17 | +Pour résoudre ce problème, vous devriez soit éviter de partager des données mutables entre les threads, soit utiliser des mécanismes de synchronisation appropriés, tels que des verrous, pour protéger les données partagées. Dans ce cas, le `front_matter_dict` est modifié en place, ce qui n'est pas thread-safe. La solution consiste à créer une copie du dictionnaire avant de le modifier. Cela est déjà fait dans le code, mais il est important de comprendre pourquoi c'est nécessaire. |
| 18 | + |
| 19 | +## Contexte |
| 20 | + |
| 21 | +```python |
| 22 | + with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_THREADS) as executor: |
| 23 | + futures = [] |
| 24 | + for filename in changed_files: |
| 25 | + input_file = filename |
| 26 | + |
| 27 | + for lang in languages: |
| 28 | + |
| 29 | + print(f"Submitting translation job for {filename} to {lang}...") |
| 30 | + future = executor.submit(translate_markdown_file, input_file, os.path.join(f"_posts/{lang}", os.path.basename(filename).replace(".md", f"-{lang}.md")), lang, dry_run) |
| 31 | + futures.append(future) |
| 32 | + |
| 33 | + for future in concurrent.futures.as_completed(futures): |
| 34 | + try: |
| 35 | + future.result() |
| 36 | + except Exception as e: |
| 37 | + print(f"A thread failed: {e}") |
| 38 | +``` |
| 39 | + |
| 40 | +## Avant |
| 41 | + |
| 42 | +```python |
| 43 | +def translate_front_matter(front_matter, target_language, input_file): |
| 44 | + print(f" Translating front matter for: {input_file}") |
| 45 | + if not front_matter: |
| 46 | + print(f" No front matter found for: {input_file}") |
| 47 | + return "" |
| 48 | + try: |
| 49 | + front_matter_dict = {} |
| 50 | + if front_matter: |
| 51 | + front_matter_dict = yaml.safe_load(front_matter) |
| 52 | + print(f" Front matter after safe_load: {front_matter_dict}") |
| 53 | + if 'title' in front_matter_dict: |
| 54 | + print(f" Translating title: {front_matter_dict['title']}") |
| 55 | + if not (input_file == 'original/2025-01-11-resume-en.md' and target_language in ['zh', 'fr']): |
| 56 | + if isinstance(front_matter_dict['title'], str): |
| 57 | + translated_title = translate_text(front_matter_dict['title'], target_language) |
| 58 | + if translated_title: |
| 59 | + translated_title = translated_title.strip() |
| 60 | + if len(translated_title) > 300: |
| 61 | + translated_title = translated_title.split('\n')[0] |
| 62 | + front_matter_dict['title'] = translated_title |
| 63 | + print(f" Translated title to: {translated_title}") |
| 64 | + else: |
| 65 | + print(f" Title translation failed for: {input_file}") |
| 66 | + else: |
| 67 | + print(f" Title is not a string, skipping translation for: {input_file}") |
| 68 | + else: |
| 69 | + print(f" Skipping title translation for {input_file} to {target_language}") |
| 70 | + # Always set lang to target_language |
| 71 | + |
| 72 | + # Determine if the file is a translation |
| 73 | + original_lang = 'en' # Default to english |
| 74 | + if 'lang' in front_matter_dict: |
| 75 | + original_lang = front_matter_dict['lang'] |
| 76 | + |
| 77 | + if target_language != original_lang: |
| 78 | + front_matter_dict['lang'] = target_language |
| 79 | + front_matter_dict['translated'] = True |
| 80 | + print(f" Marked as translated to {target_language} for: {input_file}") |
| 81 | + else: |
| 82 | + front_matter_dict['translated'] = False |
| 83 | + print(f" Not marked as translated for: {input_file}") |
| 84 | + |
| 85 | + |
| 86 | + result = "---\n" + yaml.dump(front_matter_dict, allow_unicode=True) + "---" |
| 87 | + print(f" Front matter translation complete for: {input_file}") |
| 88 | + return result |
| 89 | + except yaml.YAMLError as e: |
| 90 | + print(f" Error parsing front matter: {e}") |
| 91 | + return front_matter |
| 92 | +``` |
| 93 | + |
| 94 | +## Après |
| 95 | + |
| 96 | +```python |
| 97 | +def translate_front_matter(front_matter, target_language, input_file): |
| 98 | + print(f" Translating front matter for: {input_file}") |
| 99 | + if not front_matter: |
| 100 | + print(f" No front matter found for: {input_file}") |
| 101 | + return "" |
| 102 | + try: |
| 103 | + front_matter_dict = {} |
| 104 | + if front_matter: |
| 105 | + front_matter_dict = yaml.safe_load(front_matter) |
| 106 | + print(f" Front matter after safe_load: {front_matter_dict}") |
| 107 | + |
| 108 | + front_matter_dict_copy = front_matter_dict.copy() |
| 109 | + |
| 110 | + if 'title' in front_matter_dict_copy: |
| 111 | + print(f" Translating title: {front_matter_dict_copy['title']}") |
| 112 | + if not (input_file == 'original/2025-01-11-resume-en.md' and target_language in ['zh', 'fr']): |
| 113 | + if isinstance(front_matter_dict_copy['title'], str): |
| 114 | + translated_title = translate_text(front_matter_dict_copy['title'], target_language) |
| 115 | + if translated_title: |
| 116 | + translated_title = translated_title.strip() |
| 117 | + if len(translated_title) > 300: |
| 118 | + translated_title = translated_title.split('\n')[0] |
| 119 | + front_matter_dict_copy['title'] = translated_title |
| 120 | + print(f" Translated title to: {translated_title}") |
| 121 | + else: |
| 122 | + print(f" Title translation failed for: {input_file}") |
| 123 | + else: |
| 124 | + print(f" Title is not a string, skipping translation for: {input_file}") |
| 125 | + else: |
| 126 | + print(f" Skipping title translation for {input_file} to {target_language}") |
| 127 | + # Always set lang to target_language |
| 128 | + |
| 129 | + front_matter_dict_copy['lang'] = target_language |
| 130 | + front_matter_dict_copy['translated'] = True |
| 131 | + |
| 132 | + result = "---\n" + yaml.dump(front_matter_dict_copy, allow_unicode=True) + "---" |
| 133 | + print(f" Front matter translation complete for: {input_file}") |
| 134 | + return result |
| 135 | + except yaml.YAMLError as e: |
| 136 | + print(f" Error parsing front matter: {e}") |
| 137 | + return front_matter |
| 138 | +``` |
0 commit comments